我注意到有很多软件包允许你传递在调用函数的上下文中甚至可能无效的符号名称.我想知道它是如何工作的以及如何在我自己的代码中使用它?
这是ggplot2的一个例子:
a <- data.frame(x=1:10,y=1:10)
library(ggplot2)
qplot(data=a,x=x,y=y)
Run Code Online (Sandbox Code Playgroud)
x并且y在我的命名空间中不存在,但ggplot理解它们是数据框的一部分,并将它们的评估推迟到它们有效的上下文中.我尝试过做同样的事情:
b <- function(data,name) { within(data,print(name)) }
b(a,x)
Run Code Online (Sandbox Code Playgroud)
然而,这失败了:
Error in print(name) : object 'x' not found
Run Code Online (Sandbox Code Playgroud)
我究竟做错了什么?这是如何运作的?
注意:这不是将变量名称传递给r中的函数的副本
我需要对Haskell的懒惰做一些澄清.
如果我有这个功能:
myFunction arg
| arg == 1 = a
| arg == 2 = a*b
| arg == 3 = b+c
| otherwise = (a+b)*c
where
a = ...
b = ...
c = ...
d = ...
Run Code Online (Sandbox Code Playgroud)
当我打电话时myFunction 1,Haskell将只评估a = ...函数,既不是b也不是c,也不是d.
但如果我写
myFunction arg
| arg == 1 = a
| arg == 2 = a*b
| arg == 3 = b+c
| otherwise = (a+b)*c
where
(a,b,c,d) …Run Code Online (Sandbox Code Playgroud) lazy="true"和lazy="proxy"nhibernate有什么区别?
请考虑以下代码段:
object A {
val b = c
val c = "foo"
}
println( A.b ) // prints "null"
Run Code Online (Sandbox Code Playgroud)
作为更大程序的一部分,这将导致运行时失败.编译器显然允许从'b'到(未初始化)'c'的前向引用,但是'b'留下c的原始空值.为什么允许这样做?是否有可以从此功能中受益的编程方案?
将代码更改为直接序列并且行为更改:
val b = c
val c = "foo"
println( b ) // prints "foo"
Run Code Online (Sandbox Code Playgroud)
为什么行为不同?为什么这甚至有效?谢谢.
更新1:
问题出现了我如何运行第二个例子.我简化了设置并使用最新的Scala插件在IntelliJ IDEA 10.5.2中使用Scala 2.9.0.1编译它.这是确切的代码,在一个新创建的和其他空项目中,我用它来测试它,它在这种环境中编译并运行良好:
package test
object Main {
def main( args: Array[String] ) {
val b = c
val c = "foo"
println( b ) // prints "foo"
}
}
Run Code Online (Sandbox Code Playgroud)
对于它的价值,IDEA还认为(当我点击"通过"对val b = c中的'c'的引用时)我指的是(c)的(后面)声明.
我知道Java在这种情况下有智能/懒惰的评估:
public boolean isTrue() {
boolean a = false;
boolean b = true;
return b || (a && b); // (a && b) is not evaluated since b is true
}
Run Code Online (Sandbox Code Playgroud)
但是关于:
public boolean isTrue() {
boolean a = isATrue();
boolean b = isBTrue();
return b || a;
}
Run Code Online (Sandbox Code Playgroud)
isATrue()即使isBTrue()返回true 也会被调用?
我想知道如何使用惰性函数语言实现调试.
你能使用断点,印刷语句和传统技术吗?这甚至是个好主意吗?
我的理解是纯函数式编程不允许副作用,monad除外.
执行顺序也不能保证.
您是否需要为要测试的每个代码段编写一个monad?我想从这个领域更有经验的人那里了解这个问题.
这个问题源于一个挑战布伦特Yorgey提出在OPLSS:写一个函数f :: (Int -> Int) -> Bool区分f undefined的f (\x -> undefined).我们所有的答案要么被使用,seq要么类似于令人厌恶的爆炸模式seq.例如:
f :: (Int -> Int) -> Bool
f g = g `seq` True
*Main> f undefined
*** Exception: Prelude.undefined
*Main> f (\x -> undefined)
True
Run Code Online (Sandbox Code Playgroud)
该GHC评上seq说,
e1 `seq` e2
Run Code Online (Sandbox Code Playgroud)
过去常见的
case e1 of { _ -> e2 }
Run Code Online (Sandbox Code Playgroud)
所以我尝试了手工制作.它不起作用:
f' g = case g of { _ -> True }
*Main> f' undefined
True
*Main> …Run Code Online (Sandbox Code Playgroud) 根据http://www.reddit.com/r/programming/comments/gwqa2/the_real_point_of_laziness/c1rslxk
有些算法并不是以一种急切的语言终止,而是以懒惰的方式终止,(对我来说,这是一种轻微的震惊),反之亦然.
前者当然是众所周知的,但后者让我感到震惊,如果是真的,那将远远超过一个温和的震撼.
有谁知道一种算法以一种渴望的语言终止而不是一种懒惰的语言?
我在Haskell中进行懒惰评估的困难之一是难以推断内存使用情况.我认为复制thunk的能力会让我更容易.这是一个例子.
让我们创建一个非常大的列表:
let xs = [1..10000000]
Run Code Online (Sandbox Code Playgroud)
现在,让我们创建一个不好的函数:
bad = do
print $ foldl1' (+) xs
print $ length xs
Run Code Online (Sandbox Code Playgroud)
没有优化,这会占用几十MB的内存.垃圾收集器在折叠期间不能解除分配xs,因为稍后需要计算长度.
是否有可能重新实现此功能:
good = do
(xs1,xs2) <- copyThunk xs
print $ foldl1' (+) xs1
print $ length xs2
Run Code Online (Sandbox Code Playgroud)
现在,xs1和xs2将表示相同的值,但在内存中也相互独立,因此垃圾收集器可以在折叠期间解除分配以防止内存浪费.(我认为这会稍微增加计算成本吗?)
显然在这个简单的例子中,重构代码可以很容易地解决这个问题,但似乎并不总是很明显如何重构.或者有时重构会大大降低代码清晰度.
我现在正在阅读Graham Hutton编写的Haskell编程.
在第40页,提出了玩具素性测试:
factors :: Int -> [Int]
factors n = [x | x <- [1..n], n `mod` x == 0]
prime :: Int -> Bool
prime n = factors n == [1,n]
Run Code Online (Sandbox Code Playgroud)
然后,作者继续解释如何
"确定一个数字不是素数不需要函数素数来产生它的所有因子,因为在惰性求值下
False,只要产生一个或多个数字本身以外的任何因子,就会返回结果"
作为来自C和Java的人,我发现这令人震惊.我希望factors调用首先完成,将结果保存在堆栈中并将控制传递给调用函数.但显然这里正在执行一个非常不同的程序:列表理解必须有一个循环,factors并且prime正在检查添加到因子列表中的每个新元素的相等性检查.
这怎么可能?这对于程序的执行顺序是否更难以推理?
lazy-evaluation ×10
haskell ×4
debugging ×1
evaluation ×1
java ×1
nhibernate ×1
proxy ×1
r ×1
scala ×1
semantics ×1