Types Clojure:根据用户输入创建映射

Types Clojure:根据用户输入创建映射,types,clojure,Types,Clojure,我现在正在学习Clojure,我很难进入功能性思维 我有一个映射,我正试图从命令行填充它。这是我目前掌握的代码: (ns dungeonworld.core (:gen-class)) (def pc {:Name "" :Class "" :Race "" :Look "" :Str 0 :Dex 0 :Con 0 :Wis 0 :Int 0

我现在正在学习Clojure,我很难进入功能性思维

我有一个映射,我正试图从命令行填充它。这是我目前掌握的代码:

(ns dungeonworld.core
  (:gen-class))

(def pc {:Name ""
         :Class ""
         :Race ""
         :Look ""
         :Str 0
         :Dex 0
         :Con 0
         :Wis 0
         :Int 0
         :Cha 0
         })

(defn getVals 
  []
  (println "Enter Name: ")
  (assoc pc :Name (read-line))
  (println "Enter Class: ")
  (assoc pc :Race (read-line))
  (println "Enter Look: ")
  (assoc pc :Look (read-line)))

(defn -main
  "Create a Dungeon World character map"
  [& args]
  (getVals))
但是,它只更新最后一个条目:Look

问题: 如何以更实用的Clojure-y方式实现我想要实现的目标,以及它为什么只更新最后一个map元素?地图类型正确吗

非常感谢

assoc返回包含新映射的新映射。您没有将这些新贴图指定给任何对象,因此它们将被丢弃。您需要跟踪中间映射-从输入序列重复更新状态的一种方法是使用reduce:

assoc返回包含新映射的新映射。您没有将这些新贴图指定给任何对象,因此它们将被丢弃。您需要跟踪中间映射-从输入序列重复更新状态的一种方法是使用reduce:

getVals函数将创建三个新映射,并仅返回最后一个映射。功能性思维与强制性思维最重要的一部分是创造新的价值观,而不是就地修改价值观

另一种方法是使用三个新项目创建新地图,并将其合并到起始地图中,以获得所需地图:

(defn getVals
  []
  (merge pc {:Name (do (println "Enter Name: ")
                       (read-line))
             :Race (do (println "Enter Class: ")
                       (read-line))
             :Look (do (println "Enter Look: ")
                       (read-line))}))
在另一个答案中提到的reduce也会起作用,但此时此刻对您来说可能是一个不必要的额外概念。我认为最好从更简单的例子中学习reduce

您也可以使用atom,但它是可变状态,但在Clojure和函数式编程中,建议将其保持在绝对最小值。本例似乎不需要它。

您的getVals函数正在创建三个新映射,并仅返回最后一个映射。功能性思维与强制性思维最重要的一部分是创造新的价值观,而不是就地修改价值观

另一种方法是使用三个新项目创建新地图,并将其合并到起始地图中,以获得所需地图:

(defn getVals
  []
  (merge pc {:Name (do (println "Enter Name: ")
                       (read-line))
             :Race (do (println "Enter Class: ")
                       (read-line))
             :Look (do (println "Enter Look: ")
                       (read-line))}))
在另一个答案中提到的reduce也会起作用,但此时此刻对您来说可能是一个不必要的额外概念。我认为最好从更简单的例子中学习reduce


您也可以使用atom,但它是可变状态,但在Clojure和函数式编程中,建议将其保持在绝对最小值。这个案例似乎不需要它。

要添加到前面的答案中,您不必在Clojure中使用可变状态,就可以在内部作用域中访问某些内容。在这里,Let绑定通常很有用。例如,你可以做如下事情

(defn getVals []
 (let [user-input (fn [message]
                      (println message)
                      (read-line))
       name (user-input "Enter Name: ")
       race (user-input "Enter Class: ")
       look (user-input "Enter Look: ")]
 (merge pc {:name name :race race :look look})))
这里的效果是,绑定在let的范围内可用,而let的范围就是let的开始括号和结束括号之间的所有内容。但它仍然是不变的;如果返回值是

{:name (str name "another-name") :race name}

对name的第二个引用仍将引用原始值。还要注意此处使用的匿名助手函数。当然,这一切都取决于您是否希望在其他地方使用功能,但在顶级命名空间中不添加必须跟踪的内容的帮助器函数可能会有所帮助。

要添加到前面的答案中,您不必在Clojure中使用可变状态,就可以在内部范围中访问某些内容。在这里,Let绑定通常很有用。例如,你可以做如下事情

(defn getVals []
 (let [user-input (fn [message]
                      (println message)
                      (read-line))
       name (user-input "Enter Name: ")
       race (user-input "Enter Class: ")
       look (user-input "Enter Look: ")]
 (merge pc {:name name :race race :look look})))
这里的效果是,绑定在let的范围内可用,而let的范围就是let的开始括号和结束括号之间的所有内容。但它仍然是不变的;如果返回值是

{:name (str name "another-name") :race name}

对name的第二个引用仍将引用原始值。还要注意此处使用的匿名助手函数。当然,这一切都取决于您是否希望在其他地方使用功能,但是在顶级名称空间中,如果有帮助函数,而这些函数不会添加到您必须跟踪的内容中,则会很有帮助。

我一直在努力彻底理解这段代码。是reduce函数多次调用prompt,对吗?如果我需要一个持久变量,我可以交换!我想这是一个新的原子?我一直在努力彻底理解这段代码。是reduce函数多次调用prompt,对吗?如果我需要一个持久变量,我可以交换!我想它会变成一个新的原子吧?