vik*_*ata 6 variables haskell immutability assign
刚开始使用Haskell,据说除了IO包外,Haskell中的所有内容都是“不可变的”。因此,当我将名称绑定到某物时,它总是不可变的?问题,如下:
Prelude> let removeLower x=[c|c<-x, c `elem` ['A'..'Z']]
Prelude> removeLower "aseruiiUIUIdkf"
"UIUI"
Run Code Online (Sandbox Code Playgroud)
所以在这里:
1. “removeLower" is an immutable? Even it's a function object?
But I can still use "let" to assign something else to this name.
2. inside the function "c<-x" seems that "c" is a variable.
It is assigned by list x's values.
Run Code Online (Sandbox Code Playgroud)
我使用的是 C 语言中的“变量”这个词,不知道 Haskell 是如何命名它的所有名称的?
谢谢。
如果您熟悉 C,请考虑声明变量和为其分配值之间的区别。例如,您可以自己声明一个变量,然后再分配给它:
int i;
i = 7;
Run Code Online (Sandbox Code Playgroud)
或者您可以同时声明一个变量并分配初始值:
int i = 7;
Run Code Online (Sandbox Code Playgroud)
在任何一种情况下,您都可以通过在第一次初始化或赋值后再次赋值来改变变量的值:
int i = 7; // Declaration and initial assignment
i = 5; // Mutation
Run Code Online (Sandbox Code Playgroud)
Haskell 中的赋值完全像第二个例子一样——带初始化的声明:
我加粗并超链接“范围”,因为它是这里的第二个关键组件。这是您的问题之一:
“removeLower” 是不可变的?即使它是一个函数对象?但我仍然可以使用“let”为这个名称分配其他东西。
绑定removeLower到您在示例中定义的函数后,名称removeLower将始终引用该定义范围内的该函数。这很容易在解释器中演示。首先,让我们定义一个函数foo:
Prelude> let foo x = x + 2
Prelude> foo 4
6
Run Code Online (Sandbox Code Playgroud)
现在我们定义一个bar使用foo:
Prelude> let bar x = foo (foo x)
Prelude> bar 4
8
Run Code Online (Sandbox Code Playgroud)
现在我们“重新定义”foo为不同的东西:
Prelude> let foo x = x + 3
Prelude> foo 4
7
Run Code Online (Sandbox Code Playgroud)
现在你认为会发生什么bar?
Prelude> bar 4
8
Run Code Online (Sandbox Code Playgroud)
它保持不变!因为“重定义”foo不会改变任何东西——它只是说,在由“重定义”创建的新作用域中,名称foo代表添加三的函数。的定义bar是在更早的范围 where 中进行的foo x = x + 2,所以这就是名称foo在 的定义中的含义bar。的原始值foo没有被“重新定义”破坏或改变。
在 Haskell 程序和 C 程序中一样,在程序的不同范围内,相同的名称仍然可以引用不同的值。这就是使“变量”可变的原因。不同之处在于,在 Haskell 中,您永远不能在一个范围内改变变量的值。但是,您可以隐藏定义——在某种意义上,变量的使用将引用该名称的“最近”定义。(在解释器的情况下,该let变量的最新声明。)
现在,除此之外,这里是 Haskell 中存在的用于变量绑定(“赋值”)的语法。首先,模块中有顶级声明:
module MyLibrary (addTwo) where
addTwo :: Int -> Int
addTwo x = x + 2
Run Code Online (Sandbox Code Playgroud)
这里的名字addTwo是用给定的函数作为它的值来声明的。顶级声明可以在where块中包含私有的辅助声明:
addSquares :: Integer -> Integer
addSquares x y = squareOfX + squareOfY
where square z = z * z
squareOfX = square x
squareOfY = square y
Run Code Online (Sandbox Code Playgroud)
然后是let ... in ...表达式,它允许您为任何表达式声明一个局部变量:
addSquares :: Integer -> Integer
addSquares x y =
let square z = z * z
squareOfX = square x
squareOfY = square y
in squareOfX + squareOfY
Run Code Online (Sandbox Code Playgroud)
然后是do-notation,它有自己的语法来声明变量:
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 ++ "!")
Run Code Online (Sandbox Code Playgroud)
所述var <- action分配是由一个所产生的值的动作(例如,读取来自标准输入的线),而let var = expr受让人,其由产生的值函数(例如,串联一些字符串)。需要注意的是,let在一个do块是不是同样的事情let ... in ...上面!
最后,在列表理解中,您将获得与do-notation 中相同的赋值语法。
它指的是一元绑定运算符>>=。您只是不需要显式编写 lambda 作为右侧参数。列表压缩将被编译为定义的单子操作。这意味着与单子环境中的完全相同。
事实上,您可以通过简单的调用来替换列表压缩filter:
filter (`elem` ['A' .. 'Z']) x
Run Code Online (Sandbox Code Playgroud)
<-为了更清楚地回答您有关语法结构的问题:
[c| c <- x]
Run Code Online (Sandbox Code Playgroud)
是相同的
do c <- x
return c
Run Code Online (Sandbox Code Playgroud)
是相同的
x >>= \c -> return c
Run Code Online (Sandbox Code Playgroud)
是相同的
x >>= return
Run Code Online (Sandbox Code Playgroud)
请考虑 Haskell 的官方文档以供进一步阅读:https://hackage.haskell.org/package/base-4.8.2.0/docs/Control-Monad.html#v :-62--62--61-