Clojure映射功能以什么方式?

时间:2019-03-22 13:56:08

标签: function clojure

"Maybe not"里奇·希基(Rich Hickey)说:

  

地图是(数学上的)函数!

     

在Clojure中,我们可以直接编写和调用

     

({:a 1:b 2}:b)=> 2

但是我感觉它们实际上不是一流的Clojure函数,还是它们?

我可以用关键字或相反的方式调用地图:

user=> (:b {:a 1 :b 2 :c 3})
2
user=> ({:a 1 :b 2 :c 3} :b)
2

但我似乎无法使用Apply的任何一种方式:

user=> (apply #(:b %) {:a 1 :b 2 :c 3})
ArityException Wrong number of args (3) passed to: user/eval1762/fn--1763  clojure.lang.AFn.throwArity (AFn.java:429)

user=> (apply #({:a 1 :b 2 :c 3} %) :b)
IllegalArgumentException Don't know how to create ISeq from: clojure.lang.Keyword  clojure.lang.RT.seqFrom (RT.java:542)

我也不能直接将关键字应用于地图:

user=> (apply {:a 1 :b 2 :c 3} :b)
IllegalArgumentException Don't know how to create ISeq from: clojure.lang.Keyword  clojure.lang.RT.seqFrom (RT.java:542)

那么它们是仅在数学意义上起作用,还是在应用类似于“正常” clojure函数的关键字意义上呢?

3 个答案:

答案 0 :(得分:9)

就像您的前两个示例一样,

地图键的功能。地图实现了IFn接口,就像“常规” Clojure函数一样。

apply在您的示例中不起作用的原因是由于在第二个位置传递了“序列”参数。

(apply #(:b %) {:a 1 :b 2 :c 3})

在该示例中,map参数被转换为键/值向量/元组的序列,因此可以将它们应用#(:b %)(无论如何都无法工作,因为该匿名函数仅接受一个参数)。地图变成序列并用作函数参数时的外观如下:

user=> (seq {:a 1 :b 2 :c 3})
([:a 1] [:b 2] [:c 3])

第二个示例无效,因为:b不是序列-它是一个关键字。但这可以工作:

user=> (apply {:a 1 :b 2 :c 3} [:b])
2

请注意,使用apply和一系列关键字来调用map-as-function并不是很实际。

答案 1 :(得分:8)

mathematical function是一组有序对,因此没有相同的第一个元素的不同对。这样定义的数学函数仅需一个参数。

Clojure映射是不言而喻的数学函数,因为不存在具有相同键的不同键值对。此外,Clojure映射具有有限域(keys的集合)和范围/共域(values的集合)。

正如@TaylorWood所述,Clojure映射是 Clojure 函数,因为它们实现了clojure.lang.IFn。他们这样做是因为运算符会产生给定键的值,因此与其对数学函数的解释一致。

您的apply语法错误。您可以写

user=> (apply {:a 1, :b 2} [:a])
1

user=> (apply {:a 1, :b 2} :a []) 
1

您可以内联提供该函数的一些参数(apply的第一个参数)。其余部分包括作为apply的最后一个参数的序列。由于此处仅存在一个参数,因此我们可以在终端序列中将其内联或单独使用。

类似的考虑也适用于使用关键字作为参数。


回复@AlanThompson的评论

  1. 我已经听过并阅读了问题所涉及的Rich's talk。我对函数的定义与他的观点一致。

  2. 我的说法“ “如此定义的数学函数仅接受一个参数” 显然是正确的。

  3. 您可以说我的定义不是传统的定义。但是,那是错误的。如果您阅读我们都引用的Wikipedia文章的Multivariate_function部分,则会发现

  

更正式地说,n个变量的函数是其域为   一组n元组

     

...

     

使用函数表示法时,通常会省略括号   围绕元组,写f(x1 , x2)而不是f((x1 , x 2))

换句话说,(数学)函数采用多个参数的想法是一种无害的非正式性,允许使用更简单的表示法。

编程语言在哪里?它们中的大多数都具有任意Arity的功能:对于Clojure而言,任何有限的Arity集,包括无限的Arity。

考虑Clojure函数的最简单方法是,它们采用内联参数序列,可能是空参数。可以用这种方式来表达数学函数:从事物序列到事物的映射。 但这还没有完成

答案 2 :(得分:4)

我不确定这个问题的实际目标是什么,但是从学术角度来看很有趣。

以下关键字“ function” :b是1个参数的函数:

(:b {:a 1 :b 2})  => 2

就像这个功能:

(defn get-b [m] (get m :b)) ; takes a single map arg

(get-b {:a 3 :b 33})  ; only 1 arg, so no problem
   => 33

特殊形式apply 重新编写函数调用 ,从具有单个collection参数变为具有多个单独的arg。得到之前和之后的比较:

(apply some-fn [1 2 3] )  ; using  apply
      (some-fn  1 2 3  )  ; without apply (note missing square brackets!)

因此,apply允许您从集合 中“散布” args,就像您在函数调用中将它们作为单独的单个args输入一样。如果您的收藏集只有1个元素,则使用apply效果不大:

(apply :b [{:a 3 :b 33}] ) ; a collection of 1 element
      (:b  {:a 3 :b 33}  ) ; only 1 arg, so no problem

由于功能:b仅占用1个arg,因此以下操作无效:

(apply #(:b %) [{:a 1 :b 11} {:a 2 :b 22} {:a 3 :b 33}])  ; takes only 1 arg, not 3 args

但是,您可以使用:bmap多次调用1-arg函数mapv

(mapv :b [{:a 1 :b 11} {:a 2 :b 22} {:a 3 :b 33}])  ; takes only 1 arg, not 3 args

   => [11 22 33]

相反的情况也适用相同的规则。像{:a 1 :b 2}这样的映射是1个参数的函数,因此无法使用3个args来调用它:

(apply {:a 1, :b 2, :c 3} [:c :b :a])  ; 3 args to single-arg fn, so fails

但这很好用:

(mapv {:a 1, :b 2, :c 3} [:c :b :a]) => [3 2 1]