使用map、apply函数和带引号表达式的向量进行评估。

huangapple go评论43阅读模式
英文:

Evaluation with map, apply of function, and vector of quoted expressions

问题

以下是翻译好的内容:

什么是正确思考以下代码的方式?

user=> (map #(apply println %) ['(+ 1 1)])
+ 1 1
;=> (nil)

我脑海中可能认为这应该打印出 2 而不是实际打印出来的内容。我认为引用的 '(+ 1 1) 应该在它在 [] 内部时被评估一次,然后当 apply 将其放在 println 的参数位置时,(+ 1 1) 应该再次被评估为 2

以下是我对评估的一种粗略想象:

(map #(apply println %) ['(+ 1 1)])
; --> 读取器(reader)处理
(<map-function-var> <anonymous-println-function-var> [(+ 1 1)])
; --> <map-function-var> 应用 <anonymous-println-function-var> 到参数上:
[(#(apply println %) [(+ 1 1)])]
; --> 应用参数
[(println (+ 1 1))]
; --> 评估它
[(println 2)]
; --> 打印它
2
;=> nil

我的思维模型明显多了一个评估步骤,我认为最后一个步骤中产生 2 的地方是错误的,但我不确定。是否有人可以解释我应该如何思考这个替换和评估序列?

更新:这可能与以下内容有关:

user=> (map #(apply println %) [(list "a" "b") (list "c" "d")])
a b
c d
(nil nil)

一旦 [] 的内容被评估,它们就变成了数据,当它们被传递给 #(apply println %) 时,它们被视为数据,因为 [] 的元素已经被评估,不会传递给评估器以寻找函数 "a""c"...?

英文:

What is the correct way to think about the following?

user=&gt; (map #(apply println %) [&#39;(+ 1 1)])
+ 1 1
;=&gt; (nil)

I have in my head that this should possibly print 2 and not what it does print. I have a notion that the quoted &#39;(+ 1 1) should be evaluated once to be the data (+ 1 1) when it is inside of [], and again (+ 1 1) should be evaluated to 2 when apply puts it in the argument position of println.

Here is how I am kind of picturing evaluation:

(map #(apply println %) [&#39;(+ 1 1)])
; --&gt; to reader
(&lt;map-function-var&gt; &lt;anonymous-println-function-var&gt; [(+ 1 1)])
; --&gt; &lt;map-function-var&gt; applys the &lt;anonymous-println-function-var&gt; to the args:
[(#(apply println %) [(+ 1 1)])]
; --&gt; apply the argument
[(println (+ 1 1)]
; --&gt; eval it
[(println 2]
; --&gt; print it
2
;=&gt; nil

My mental model obviously has one too many evaluations in it, and I think the last one, where 2 is produced, is where it is wrong, but I am not sure. Can someone explain how I should be thinking of this sequence of substitutions and evaluations?

Update: it may have something to do with this?

user=&gt; (map #(apply println %) [(list &quot;a&quot; &quot;b&quot;) (list &quot;c&quot; &quot;d&quot;)])
a b
c d
(nil nil)

Once the contents of [] are evaluated, they are now data, and when they are passed to #(apply println %), they are considered data, as the elements of [] have been evaluated, and will not be passed to the evaluator to try and find the function &quot;a&quot; or &quot;c&quot;... ?

答案1

得分: 2

首先,map返回一个惰性序列,不应该用它来执行副作用(比如打印)。

有方法可以强制执行惰性序列中的所有副作用(dorundoall),但在这种情况下,你可以直接使用run!doseq

至于主要问题:(+ 1 1)只是REPL打印表达式&#39;(+ 1 1)的结果的方式(该表达式的结果是一个包含一个符号和两个数字的列表)。但是,如果你只是复制粘贴该打印的表示形式到REPL中,你将得到Clojure表达式,计算结果为2

根据评估参考文章,我将尝试描述当你调用(map #(apply println %) [&#39;(+ 1 1)])时发生了什么:

  1. map被评估为 => #object[clojure.core$map]
  2. #(apply println %)被评估为 => #object[fn]
  3. [&#39;(+ 1 1)]被评估为 => [(+ 1 1)](带有一个符号和两个数字的列表的向量的打印表示)
  4. #object[clojure.core$map]被调用,传入#object[fn][(+ 1 1)]

现在,你可以查看(source map)来了解map在其参数上的确切操作(惰性,分块等等),但为了简单起见,让我们只关注实现中的这个小表达式:

(f (first s))
  1. f被评估为 => #object[fn]
  2. first被评估为 => #object[clojure.core$first]
  3. s被评估为 => [(+ 1 1)]
  4. #object[clojure.core$first]被调用,传入[(+ 1 1)] => (+ 1 1)
  5. 带有体(apply println %)#object[fn]被调用,传入(+ 1 1)
    1. apply被评估为 => #object[clojure.core$apply]
    2. println被评估为 => #object[clojure.core$println]
    3. %被评估为 => (+ 1 1)
    4. #object[clojure.core$apply]被调用,传入#object[clojure.core$println](+ 1 1)

现在,你可以查看(source apply)的相关部分:

([^clojure.lang.IFn f args]
     (. f (applyTo (seq args))))

这是一些Java互操作,但在评估的过程中,args被评估为(+ 1 1)

重要的是:(#(apply println %) &#39;(+ 1 1))等同于(apply println &#39;(+ 1 1)),这等同于(println &#39;+ &#39;1 &#39;1)(println &#39;+ 1 1)

我的观点是,在评估过程中,表达式(+ 1 1)永远不会被评估(因此你不会得到2作为结果),但一些符号和表达式会被评估为(+ 1 1)

英文:

First of all, map returns a lazy sequence and you shouldn't use it for side effects (like printing).

There are ways to force all side effects in the lazy sequence (dorun, doall), but in this case, you can directly use run! or doseq.

As for the main problem: (+ 1 1) is just the way how REPL prints the result of the expression &#39;(+ 1 1) (a result of that expression is a list with one symbol and two numbers).
But if you just copy-paste that printed representation into REPL, you will get Clojure expression evaluating to 2.

Following Evaluation reference article, I will try to describe what is going on when you call (map #(apply println %) [&#39;(+ 1 1)]):

  1. map is evaluated => #object[clojure.core$map]
  2. #(apply println %) is evaluated => #object[fn]
  3. [&#39;(+ 1 1)] is evaluated => [(+ 1 1)] (printed representation of vector with list with one symbol and two numbers)
  4. #object[clojure.core$map] is called with #object[fn] and [(+ 1 1)]

Now, you could look at (source map) to see, what exactly map does with its arguments (laziness, chunking...), but for the sake of simplicity, let's focus just on this small expression from the implementation:

(f (first s))
  1. f is evaluated => #object[fn]
  2. first is evaluated => #object[clojure.core$first]
  3. s is evaluated => [(+ 1 1)]
  4. #object[clojure.core$first] is called with [(+ 1 1)] => (+ 1 1)
  5. #object[fn] (with body (apply println %)) is called with (+ 1 1)
    1. apply is evaluated => #object[clojure.core$apply]
    2. println is evaluated => #object[clojure.core$println]
    3. % is evaluated => (+ 1 1)
    4. #object[clojure.core$apply] is called with #object[clojure.core$println] and (+ 1 1)

You could now look at (source apply)- just the relevant part:

([^clojure.lang.IFn f args]
     (. f (applyTo (seq args))))

That is some Java interop, but as part of the evaluation, args evaluate to (+ 1 1).

What is important: (#(apply println %) &#39;(+ 1 1)) is equivalent to (apply println &#39;(+ 1 1)) and that is equivalent to (println &#39;+ &#39;1 &#39;1) and (println &#39;+ 1 1).

My point is- during evaluation, the expression (+ 1 1) is never evaluated (so you won't get 2 as the result), but some symbols and expressions are evaluated to (+ 1 1).

答案2

得分: 1

无论您使用list还是quote (')在这里都没有任何区别。是的,您始终提供“data”,数据永远不会自行执行(您可以使用eval来使数据运行)。

在Lisp中,这有点棘手,因为代码就是数据,但要把它搞清楚:这就像期望System.out.println("System.exit(1)")在Java中退出您的程序一样。

所以您所想象的评估方式在apply步骤上会出现问题。

apply也只能用于数据。它是运行时操作:它接受一个参数列表,将给定的函数调用为如果参数直接给定一样。

(apply println '(+ 1 2))

实际上等同于

(println '+ 1 2)

(这是您思维方式错误的地方(在--> apply the argument步骤))

如果您不使用apply,它仍然只会打印表达式

(println '(+ 1 2))
; → (+ 1 2)
英文:

Whether you use list or quote (&#39;) makes no difference here. Yes,
you are always providing "data" and data will never execute on its
own (you can eval the data to make it run).

This is a bit more tricky in a Lisp, as code is data, but to put it over
the top: this would be like expecting
System.out.println(&quot;System.exit(1)&quot;) to exit your program in Java.

So the way you're picturing the evaluation breaks at the apply step.

apply also only works with data. It is a runtime operation: it
takes a list of arguments calls the given functions as if arguments
where given directly.

(apply println &#39;(+ 1 2))

is actually equivalent to

(println &#39;+ 1 2)

(this is where your train of thoughts is wrong (at the --&gt; apply the
argument
step))

If you'd not apply, it would still just print the form

(println &#39;(+ 1 2))
; → (+ 1 2)

huangapple
  • 本文由 发表于 2023年6月26日 09:19:49
  • 转载请务必保留本文链接:https://go.coder-hub.com/76553023.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定