Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/ruby-on-rails-4/2.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
具有lazy seq的Clojure尾递归_Clojure - Fatal编程技术网

具有lazy seq的Clojure尾递归

具有lazy seq的Clojure尾递归,clojure,Clojure,请您更宽泛、更清楚地解释一下,lazy seq如何按照本文档页面使尾部递归“安全” 如果您查看lazy seq的实现,您将看到它确实返回了一个闭包(一个保持上下文的函数): 因此,在(cons n(正数(inc n))中对正数的调用不会立即计算,而是延迟到调用闭包为止。如果查看lazy seq的源代码,您会注意到它是一个将其参数打包到函数体中的宏: user=> (source lazy-seq) (defmacro lazy-seq "Takes a body of express

请您更宽泛、更清楚地解释一下,
lazy seq
如何按照本文档页面使尾部递归“安全”


如果您查看
lazy seq
的实现,您将看到它确实返回了一个闭包(一个保持上下文的函数):


因此,在
(cons n(正数(inc n))
中对
正数的调用不会立即计算,而是延迟到调用闭包为止。

如果查看
lazy seq
的源代码,您会注意到它是一个将其参数打包到函数体中的宏:

user=> (source lazy-seq)
(defmacro lazy-seq
  "Takes a body of expressions that returns an ISeq or nil, and yields
  a Seqable object that will invoke the body only the first time seq
  is called, and will cache the result and return it on all subsequent
  seq calls. See also - realized?"
  {:added "1.0"}
  [& body]
  (list 'new 'clojure.lang.LazySeq (list* '^{:once true} fn* [] body)))
产生了这样的结果:

user=> (macroexpand '(lazy-seq (cons 1 (lazy-seq [2 3 4]))))
(new clojure.lang.LazySeq (fn* [] (cons 1 (lazy-seq [2 3 4]))))
这给了你一个正在发生的事情的提示:尾部位置的执行被推迟到需要时。这是如何实现的?请看:


这两个方法执行callable以获取尾部值——如果它们看到LazySeq,也会释放其中的值。然后缓存结果。

在进行函数调用(在大多数语言中)时,会将一个新帧推送到调用堆栈上。如果这样做,则会重复生成无限的数字序列(递归或循环)您最终会溢出。通过使用延迟求值,您的递归次数仅与
take
的参数相同@JaredSmith事实上,无论
take
的参数是什么,您只向堆栈添加了一小部分恒定数量的帧。您自己很容易看出您的解释不起作用:尝试编写
(最后一个)(以1e6(范围))为例)
,注意即使JVM没有足够的空间容纳一百万个堆栈帧,您仍然可以得到正确的答案。@amalloy我的观点是正确的。
user=> (source lazy-seq)
(defmacro lazy-seq
  "Takes a body of expressions that returns an ISeq or nil, and yields
  a Seqable object that will invoke the body only the first time seq
  is called, and will cache the result and return it on all subsequent
  seq calls. See also - realized?"
  {:added "1.0"}
  [& body]
  (list 'new 'clojure.lang.LazySeq (list* '^{:once true} fn* [] body)))
user=> (macroexpand '(lazy-seq (cons 1 (lazy-seq [2 3 4]))))
(new clojure.lang.LazySeq (fn* [] (cons 1 (lazy-seq [2 3 4]))))
final synchronized Object sval(){
  if(fn != null)
    {
                sv = fn.invoke();
                fn = null;
    }
  if(sv != null)
    return sv;
  return s;
}

final synchronized public ISeq seq(){
  sval();
  if(sv != null)
    {
    Object ls = sv;
    sv = null;
    while(ls instanceof LazySeq)
      {
      ls = ((LazySeq)ls).sval();
      }
    s = RT.seq(ls);
    }
  return s;
}