我正在试图弄清楚Haskell是使用动态还是静态作用域。 我意识到,例如,如果你定义:
let x = 10
然后定义函数
let square x = x*x
你有2个不同的“x”,这是否意味着它是动态范围的?如果没有,它使用什么范围,为什么?
此外,Haskell变量是否有别名(相同内存位置/值的不同名称)?
感谢。
答案 0 :(得分:11)
Haskell(广泛地说)使用与大多数其他语言完全相同的词汇范围。
例如
x = 10
在全局范围内通过x
引用的值,而
square x = x * x
将导致x
被词法限定为函数平方。如果你认为上述形式是一种语法精确的话,它可能会有所帮助:
square = \ x -> x * x
关于你的另一个问题,我不确定你的别名是什么意思
答案 1 :(得分:6)
你的陈述中有些问题......
在你的例子中, x 是而不是 10在函数中只是一个square的参数,可以取任何值(你可以在以后指定类型)案例10,但只是在这种情况下。
以下是Curt Sampson提供的别名示例:
import Data.IORef
main :: IO ()
main = do x <- newIORef 0 -- write 0 into x
readIORef x >>= print -- x contains 0
let y = x
readIORef y >>= print -- y contains 0
writeIORef x 42 -- write 42 into x
readIORef y >>= print -- y contains 42
答案 2 :(得分:6)
仅回答问题的第二部分:
对于相同的“内存位置”,您可以有多个别名,但由于它们都是不可变的,因此在大多数情况下无关紧要。
哑巴的例子:
foo x y = x * y
bar z = foo z z
当从foo
调用bar
时,x
和y
显然都是相同的值。但由于您无法修改x
或y
,因此您甚至都不会注意到。
答案 3 :(得分:3)
由于问题的第一部分已经被其他人回答,这是第二部分:
我假设aliasing
你的意思是one name for another
。由于haskell是一种函数式语言,并且函数在任何情况下都表现为普通标识符,因此您可以这样做:
y = x
将为函数y
定义别名x
。请注意,一切都是功能。即使它看起来像“变量”,它只是一个不带参数的无效函数。类型的别名如下所示:
type Function = Double -> Double
将为类型Function
Double -> Double
答案 4 :(得分:2)
在您的示例中,x的全局定义由x的本地定义遮蔽。在Haskell中,变量的作用域由源代码的静态读取决定 - 这称为词法作用域,但可以使用隐式参数获得类似于动态作用域的东西(但这可能导致一些意外行为(我读过;从不我自己试过))。
答案 5 :(得分:2)
Haskell使用静态嵌套作用域。与具有静态嵌套作用域的其他语言相比,有点令人困惑的是,名称的范围是包含其定义之前的测试的块。例如
evens = 0 : map (+1) odds
odds = map : (+1) evens
这里''赔率'的名称在'evens'的定义范围内,尽管有一个令人惊讶的事实,即'赔率'尚未定义。 (该示例定义了偶数和奇数的两个无限列表。)
具有类似范围规则的死语言是Modula-3。但是Haskell有点棘手,因为你可以尝试在同一范围内“重新定义”一个变量,而只是引入另一个递归方程。对于首先学习ML或Scheme的人来说,这是一个陷阱:
let x = 2 * n
x = x + 1 -- watch out!
这是非常好的ML或Scheme let *,但是Haskel有方案letrec语义,没有对lambda值的限制。难怪这是棘手的事情!
答案 6 :(得分:1)
简明扼要地总结其他答案:
x = 1; y = x
一样简单,但通常不重要,因为事情是不可变的。您在示例中使用的let
语法看起来就像是在交互式ghci>
提示符下。交互模式下的所有内容都发生在IO monad中,因此事情看起来可能比正常情况更容易变化。
答案 7 :(得分:0)
好吧,正如我认为人们已经说过的那样,Haskell没有大多数其他语言中的变量,它只有表达式。在您的示例中let x = 10
x是一个总是求值为10的表达式。虽然您可以使用作用域规则通过将x定义为另一个表达式来隐藏它,但您实际上无法在以后更改x的值。 / p>
答案 8 :(得分:0)
是的,Haskell有别名。试试这个小程序:
import Data.IORef
main :: IO ()
main = do x <- newIORef 0 -- write 0 into x
readIORef x >>= print -- x contains 0
let y = x
readIORef y >>= print -- y contains 0
writeIORef x 42 -- write 42 into x
readIORef y >>= print -- y contains 42