Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/clojure/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何使用;更新于;在Clojure?_Clojure - Fatal编程技术网

如何使用;更新于;在Clojure?

如何使用;更新于;在Clojure?,clojure,Clojure,我试图使用Clojure的update in函数,但我似乎不明白为什么需要传入函数?update in采用函数,因此您可以根据旧值更简洁地更新给定位置的值。例如,而不是: (assoc-in m [list of keys] (inc (get-in m [list of keys]))) 你可以写: (update-in m [list of keys] inc) 当然,如果新值不依赖于旧值,中的assoc就足够了,您不需要使用中的更新这不是对您问题的直接回答,但是像updatein这样的

我试图使用Clojure的update in函数,但我似乎不明白为什么需要传入函数?

update in
采用函数,因此您可以根据旧值更简洁地更新给定位置的值。例如,而不是:

(assoc-in m [list of keys] (inc (get-in m [list of keys])))
你可以写:

(update-in m [list of keys] inc)

当然,如果新值不依赖于旧值,中的
assoc就足够了,您不需要使用
中的
更新

这不是对您问题的直接回答,但是像
updatein
这样的函数之所以能够存在,一个原因是它能够“就地”更新映射中的值,这不仅是为了方便,也是为了提高效率。也就是说,而不是

  • 在地图上寻找钥匙
  • 查找对应的键值元组
  • 提取价值
  • 基于当前值计算新值
  • 在地图上寻找钥匙
  • 查找对应的键值元组
  • 以及覆盖元组中的值或用新元组替换元组
相反,我们可以想象一种算法,它将省略对密钥的第二次搜索:

  • 在地图上寻找钥匙
  • 找到对应的键值元组
  • 提取值
  • 基于当前值计算新值
  • 并覆盖元组中的值

遗憾的是,没有执行此“就地”更新。它用于提取和替换。除非
assoc
正在使用上次查找的键和相应的键值元组的缓存,否则对
assoc
的调用最终必须再次查找该键。

您看到的一个实际示例

键入此代码段(在REPL中):

请注意,
:您的钥匙是新的。因此传递给lambda的
:yourkey的值是
null
cons
1
作为列表中的单个元素。现在请执行以下操作:

(def my-map (update-in my-map [:yourkey] #(cons 25 %)))
;;{:yourkey (25 1), :useless-key "key"}
也就是说,在第二部分中,匿名函数将列表(即:yourkey的值)作为参数,并将其加上25

由于我们的
my map
是不可变的,因此
update in
将始终返回地图的新版本,让您使用给定键的旧值执行某些操作


希望有帮助

我认为简单的答案是,传递给update in的函数允许您在单个步骤中更新值,而不是3步(查找、计算新值、设置)

巧合的是,就在今天,我在Howard Lewis Ship的Clojure演示中遇到了更新的使用:

(def in-str "this is this")
(reduce 
  (fn [m k] (update-in m [k] #(inc (or % 0)))) 
  {} 
  (seq in-str))

==> {\space 2, \s 3, \i 3, \h 2, \t 2}
每次调用update in都会将一个字母作为键,在映射中查找它,如果在映射中找到它,则增加字母计数(否则将其设置为1)。reduce通过从空映射{}开始并使用输入字符串中的连续字符重复应用更新来驱动进程。结果是一张字母频率图。圆滑的

注1:clojure.core/frequencies类似,但使用assoc!而不是更新


注2:您可以将#(inc(或%0))替换为(fnil inc 0)。从这里开始:

您让它听起来好像您的就地变量和实际实现之间的唯一区别是就地变量只查找一次键(这意味着
updatein
可以在clojure的更高版本中更改,以便在不破坏任何现有代码的情况下使用就地变量)。然而,一个更重要的区别是,就地变体必然要对地图进行变异,而
中的更新
不会(而且,鉴于变异方法的名称中通常有
),因此,即使是非破坏性版本也可以利用知道“命中”位置的优势;即使是
assoc
也需要确定它不会复制原始映射的哪一部分,而是提供更新的值。很好,+1为核心Clojure库提供具体的性能改进建议!我不认为这是一种改进——我认为这是在修复一个bug。此外,补偿是这样一个事实:额外的查找只发生在键树中的叶更新时。也就是说,对(get)的无关调用不会从搜索的根节点重新启动,只会在最后一个节点的级别上重新启动。@andrewcooke好的算法在任何语言中都是相关的。我不得不承认,我对这个示例有点困惑。有什么疑问吗?这里的无用键只是为了显示更新的优点:您可以“遍历”映射中的嵌套级别,并将一个函数返回的值作为参数,该函数将旧值(如果有)作为参数。该函数的结果是新的输入值。调用中的每次更新都会返回一个带有新值的新映射,这就是为什么示例总是定义my map,REPL.Markc中允许重新分配。这是一个惊人的模式!无论何时,只要您想在处理当前seq后创建一个新类型的seq(在上述情况下是str的映射),请使用reduce!谢谢你的说明。
(def in-str "this is this")
(reduce 
  (fn [m k] (update-in m [k] #(inc (or % 0)))) 
  {} 
  (seq in-str))

==> {\space 2, \s 3, \i 3, \h 2, \t 2}