修改名單上有字符在Haskell
left = ['h', 'e', 'l', 'l', 'o']
和功能
typer :: [Char] -> Char -> [Char]
typer left c = left ++ [c]
這似乎如果我輸入typer left 'a'
到ghci
返回"helloa"
工作的列表,但是,如果我再嘗試閱讀left
它返回"hello"
。 "a"
去哪了?無論如何操縱left
使用該功能?
修改名單上有字符在Haskell
left = ['h', 'e', 'l', 'l', 'o']
和功能
typer :: [Char] -> Char -> [Char]
typer left c = left ++ [c]
這似乎如果我輸入typer left 'a'
到ghci
返回"helloa"
工作的列表,但是,如果我再嘗試閱讀left
它返回"hello"
。 "a"
去哪了?無論如何操縱left
使用該功能?
Haskell函數是pure functions,這意味着它們不能具有副作用,如修改變量。您的typer
函數在其參數上計算基於的結果,但它沒有(也不能)實際修改其參數。
作爲類比,在數學,如果Ñ是4,則SQRT(Ñ)爲2,但Ñ仍然是你已經計算其平方根即使經過4。
如果你想捕捉你的函數調用的結果,所以你以後可以使用它,把它分配給另一個變量:
right = typer left 'a'
都能跟得上! Haskell中的值是不可變的。
你聲明瞭left
等於"hello"
,它將總是等於"hello"
。您可以在更近的範圍內(例如具有名爲left
的函數參數,這將使全局定義的left
在函數體中不可見)隱藏該特定的left
值,並使用一個名爲left
的新令牌。然而,沒有辦法改變你聲明的left
值[1]。
A 新字符串由typer
返回,如果要保留"helloa"
,則必須保留該值。
[1]在GHCi中,您可以通過重新定義它們來隱藏以前的定義。這悄悄地重新指定現有名稱以指向新的價值。您不能在Haskell源文件中執行此操作。
關於你最後的陳述:你*可以*在'do'塊中用連續的'let'語句重新定義變量,這隱藏了同一個名字的早期定義。在這方面,GHCi REPL類似於一個大的「do」塊。 – Wyzard
對,這些是隱式嵌套的作用域,你可以通過在純代碼中明確地嵌套'let..in'和'where'塊來做同樣的事情。在最新的GHCi中,新手可能會感到困惑,因爲'let'關鍵字在綁定中不再需要,而且看起來像是在REPL中定期進行Haskell聲明。 –
通常不,不。通常,Haskell變量不可變。然而,有些庫包含可變變量。例如,Data.IORef
包含IORef
,這是可變的。我將在下面解釋如何使用它們,但作爲一般規則,使用我會推薦它,而不是以更多功能的方式編寫代碼。
隨着IOREF,你的函數typer
可以改寫
typer :: IORef [Char] -> Char -> IO()
typer left c = writeIORef left . (++[c]) =<< readIORef left
讓我們看看它的不同部分。
readIORef left
從IORef
拉出值"hello"
並把它封裝在IO
單子。f =<< x
需要x
,其被包裹在一個單子,解開它,並將其作爲一個參數傳遞給f
,但只有如果我們保證f
返回包裹在同一單子的值。這裏,x
是"hello"
包裹在IO
和f
是writeIORef left . (++[c])
。.
將功能writeIORef left
和(++[c])
組合成一個,以便在writeIORef left
之前應用(++[c])
。(++[c])
追加字符c
。writeIORef left
返回一個包含在IO
(IO()
)中的空動作,但其副作用是修改了IORef
left
的值。由於它返回包含在IO
中的值,因此我們對=<<
所做的承諾已完成。利用上述功能,代碼
main :: IO()
main = do x <- newIORef "hello"
typer x 'a'
readIORef x >>= print
應該產生所期望的效果。
從函數返回的值不是*您傳遞給它的參數。 –
歡迎來到haskell,其值不可變 – jakubdaniel
爲什麼你期望函數修改'left'?爲什麼不'c'例如? –