"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函数的关键字意义上呢?
答案 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的评论
我已经听过并阅读了问题所涉及的Rich's talk。我对函数的定义与他的观点一致。
我的说法“ “如此定义的数学函数仅接受一个参数” 显然是正确的。
您可以说我的定义不是传统的定义。但是,那是错误的。如果您阅读我们都引用的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
但是,您可以使用:b
或map
多次调用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]