如果您熟悉 C,请考虑以下之间的区别:宣告一个变量和指派对它的价值。例如,您可以单独声明一个变量,然后分配给它:
int i;
i = 7;
或者你可以声明一个变量并同时分配初始值:
int i = 7;
无论哪种情况,您都可以mutate第一次初始化或赋值后再次赋值给变量的值:
int i = 7; // Declaration and initial assignment
i = 5; // Mutation
Haskell 中的赋值工作方式与第二个示例类似——带有初始化的声明:
- 你声明一个变量;
- Haskell 不允许未初始化的变量,因此您需要在声明中提供一个值;
- 没有突变,因此声明中给出的值将是该变量在其整个过程中的唯一值scope https://en.wikipedia.org/wiki/Scope_(computer_science).
我将“范围”加粗并加了超链接,因为它是这里的第二个关键组成部分。这就是你的问题之一:
“removeLower”是不可变的?即使它是一个函数对象?但我仍然可以使用“let”为该名称分配其他内容。
绑定后removeLower
您在示例中定义的函数的名称removeLower
将始终引用该函数在该定义的范围内。这很容易在解释器中演示。首先我们定义一个函数foo
:
Prelude> let foo x = x + 2
Prelude> foo 4
6
现在我们定义一个bar
使用foo
:
Prelude> let bar x = foo (foo x)
Prelude> bar 4
8
现在我们“重新定义”foo
到不同的东西:
Prelude> let foo x = x + 3
Prelude> foo 4
7
现在你认为会发生什么bar
?
Prelude> bar 4
8
它仍然是一样的!因为“重新定义”foo
不mutate任何东西——它只是说,在“重新定义”创建的新范围内, 名字foo
代表三相加的函数。的定义bar
是在早期范围内进行的,其中foo x = x + 2
,这就是名字的意思foo
该定义中有bar
。的原始值foo
没有被“重新定义”破坏或变异。
在 Haskell 程序中和在 C 程序中一样,相同的名称仍然可以在程序的不同作用域中引用不同的值。这就是“变量”可变的原因。不同之处在于,在 Haskell 中,你永远无法在一个范围内改变变量的值。你可以shadow然而,定义——变量的使用在某种意义上将引用该名称的“最近”定义。 (就口译员而言,最新的let
该变量的声明。)
现在,抛开这些,下面是 Haskell 中存在的语法:变量绑定(“任务”)。首先,模块中有顶级声明:
module MyLibrary (addTwo) where
addTwo :: Int -> Int
addTwo x = x + 2
这里是名字addTwo
使用给定函数作为其值进行声明。顶级声明可以有私有的辅助声明where
block:
addSquares :: Integer -> Integer
addSquares x y = squareOfX + squareOfY
where square z = z * z
squareOfX = square x
squareOfY = square y
然后就是let ... in ...
表达式,允许您为任何表达式声明局部变量:
addSquares :: Integer -> Integer
addSquares x y =
let square z = z * z
squareOfX = square x
squareOfY = square y
in squareOfX + squareOfY
然后就是do
- 具有自己的声明变量语法的符号:
example :: IO ()
example = do
putStrLn "Enter your first name:"
firstName <- getLine
putStrLn "Enter your lasst name:"
lastName <- getLine
let fullName = firstName ++ " " ++ lastName
putStrLn ("Hello, " ++ fullName ++ "!")
The var <- action
分配一个由产生的值action(例如,从标准输入读取一行),而let var = expr
分配一个由 a 产生的值function(例如,连接一些字符串)。请注意,let
in a do
块与块不是同一件事let ... in ...
从上面!
最后,在列表理解中,您将获得与以下相同的赋值语法do
-符号。