我最近在整个应用程序中开始使用Lazy,我想知道在使用时是否有任何明显的消极方面需要考虑Lazy<T>?
我试图Lazy<T>尽可能经常使用,主要是为了帮助减少加载但非活动插件的内存占用.
GHC如何处理多个线程(显式线程或评估spark的内部线程)访问的thunk?是否会发生多个线程评估同一个thunk,重复工作?或者,如果thunks同步,如何,这样性能不会受到影响?
我习惯于从Haskell那里进行懒惰的评估,并且发现自己因为我已经正确地使用了懒惰的评估而感到厌烦.这实际上是非常具有破坏性的,因为我使用的其他语言主要是懒得评估一些非常笨拙的东西,通常涉及自定义迭代器的推出等等.所以只是通过获取一定的了解,其实我已经自己做了少在我原来的语言生产力.叹.
但我听说AST宏提供了另一种干净的方式来做同样的事情.我经常听到诸如"懒惰评估使宏多余"的陈述,反之亦然,主要来自于对Lisp和Haskell社区的争吵.
我已经涉足各种Lisp变种中的宏.它们看起来像是一种非常有组织的复制和粘贴代码块的方式,可以在编译时处理.他们当然不是Lispers认为的圣杯.但这几乎可以肯定是因为我无法正确使用它们.当然,让宏系统在与语言本身组合在一起的相同核心数据结构上工作是非常有用的,但它仍然基本上是一种复制和粘贴代码的有组织方式.我承认,基于与允许完全运行时更改的语言相同的AST的宏系统是强大的.
我想知道的是,如何使用宏来简明扼要地进行懒惰评估呢?如果我想逐行处理文件而不会搞砸整个事情,我只返回一个列表,其中有一个映射到它的行读取例程.这是DWIM的完美例子(尽我所能).我甚至不必考虑它.
我显然没有得到宏.我已经使用过它们并且在炒作时并没有特别留下深刻的印象.因此,我缺少一些我没有通过在线阅读文档获得的东西.有人可以向我解释这一切吗?
我有这个代码:
public class MyClass
{
public int X { get; set; }
public int Y { get; set; }
private Lazy<int> lazyGetSum = new Lazy<int>(new Func<int>(() => X + Y));
public int Sum{ get { return lazyGetSum.Value; } }
}
Run Code Online (Sandbox Code Playgroud)
给我这个错误:
字段初始值设定项不能引用非静态字段,方法或属性.
我认为通过懒惰访问非静态成员是非常合理的,怎么做?
*编辑*
接受的答案完美地解决了这个问题,但是要看到问题的详细和深入 - 总是原因,你可以阅读Joh Skeet的答案.
我看不到语言的评估策略与其纯度之间的联系。考虑到推文作者的声誉,我肯定忽略了一些东西。也许有人可以阐明一些观点。
haskell functional-programming language-implementation lazy-evaluation purity
什么是Python中的懒惰评估?
一个网站说:
在Python 3.x中,range()函数返回一个特殊的范围对象,它根据需要计算列表的元素(延迟或延迟评估):
>>> r = range(10)
>>> print(r)
range(0, 10)
>>> print(r[3])
3
Run Code Online (Sandbox Code Playgroud)
这是什么意思?
我无法理解如何在Clojure中创建一个懒惰的序列.
宏的文档对我来说一点也不清楚:
用法:(lazy-seq&body)获取一个返回ISeq或nil的表达式体,并生成一个Seqable对象,该对象仅在第一次调用seq时调用body,并将缓存结果并在随后的所有内容中返回seq电话.
我见过的所有例子似乎都是这样的:
; return everything in the sequence starting at idx n
(defn myseq-after-n [n]
(...)
)
(def my-lazy-seq
(lazy-seq (conj [init-value] (myseq-after-n 2)))
)
Run Code Online (Sandbox Code Playgroud)
所以,我没有得到的第一件事是,因为lazy-seq不在调用conj之外,它是如何阻止conj在评估时生成无限序列的?
我的第二个问题是,懒惰的序列定义总是采用这种一般形式吗?
我一直在玩Simon Marlow关于Haskell中的并行和并发编程的书中的一些例子,偶然发现了一个我不太了解的有趣行为.这真的是我试图了解GHC的一些内部运作方式.
假设我在REPL中执行以下操作:
?» let x = 1 + 2 :: Int
?» let z = (x,x)
?» :sprint x
x = _
?» :sprint z
z = (_,_)
?» seq x ()
()
?» :sprint z
z = (3,3)
Run Code Online (Sandbox Code Playgroud)
好吧,这几乎是我的预期,除了z已经被评估为WHNF.让我们编写一个类似的程序并将其放在一个文件中:
module Thunk where
import Debug.Trace
x :: Int
x = trace "add" $ 1 + 2
z :: (Int,Int)
z = (x,x)
Run Code Online (Sandbox Code Playgroud)
在GHCi中摆弄它:
?» :sprint x
x = _
?» :sprint z
z = _
?» seq x () …Run Code Online (Sandbox Code Playgroud) Haskell堆中以下值/表达式/函数的thunk是什么样的?
val = 5 -- is `val` a pointer to a box containing 5?
add x y = x + y
result = add 2 val
main = print $ result
Run Code Online (Sandbox Code Playgroud)
考虑到它的惰性评估模式,可以很好地了解这些在Haskell中的表示方式.
我很好奇无限列表的运行时性能,如下所示:
fibs = 1 : 1 : zipWith (+) fibs (tail fibs)
Run Code Online (Sandbox Code Playgroud)
这将创建一个无限的斐波那契序列表.
我的问题是,如果我做以下事情:
takeWhile (<5) fibs
Run Code Online (Sandbox Code Playgroud)
fibs评估列表中每个术语的次数是多少?似乎自从takeWhile检查列表中每个项目的谓词函数后,fibs列表将多次评估每个项目.前两个学期是免费的.当takeWhile想要评估
(<5)第3个元素时,我们将获得:
1 : 1 : zipWith (+) [(1, 1), (1)] => 1 : 1 : 3
Run Code Online (Sandbox Code Playgroud)
现在,曾经takeWhile想要评估(<5)第4个元素:fibs将再次构造列表的递归性质,如下所示:
1 : 1 : zipWith (+) [(1, 2), (2, 3)] => 1 : 1 : 3 : 5
Run Code Online (Sandbox Code Playgroud)
当我们想要评估第4个元素的值时,似乎需要再次计算第3个元素.此外,如果谓词in takeWhile很大,则表明函数正在做更多需要的工作,因为它多次评估列表中的每个前面的元素.我的分析在这里是正确的还是Haskell做了一些缓存来防止多次评估?