2017-04-20 55 views
0

修改名單上有字符在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使用該功能?

example of console output

+0

從函數返回的值不是*您傳遞給它的參數。 –

+0

歡迎來到haskell,其值不可變 – jakubdaniel

+0

爲什麼你期望函數修改'left'?爲什麼不'c'例如? –

回答

3

Haskell函數是pure functions,這意味着它們不能具有副作用,如修改變量。您的typer函數在其參數上計算基於的結果,但它沒有(也不能)實際修改其參數。

作爲類比,在數學,如果Ñ是4,則SQRT(Ñ)爲2,但Ñ仍然是你已經計算其平方根即使經過4。

如果你想捕捉你的函數調用的結果,所以你以後可以使用它,把它分配給另一個變量:

right = typer left 'a' 
2

都能跟得上! Haskell中的值是不可變的。

你聲明瞭left等於"hello",它將總是等於"hello"。您可以在更近的範圍內(例如具有名爲left的函數參數,這將使全局定義的left在函數體中不可見)隱藏該特定的left值,並使用一個名爲left的新令牌。然而,沒有辦法改變你聲明的left值[1]。

A 字符串由typer返回,如果要保留"helloa",則必須保留該值。

[1]在GHCi中,您可以通過重新定義它們來隱藏以前的定義。這悄悄地重新指定現有名稱以指向新的價值。您不能在Haskell源文件中執行此操作。

+1

關於你最後的陳述:你*可以*在'do'塊中用連續的'let'語句重新定義變量,這隱藏了同一個名字的早期定義。在這方面,GHCi REPL類似於一個大的「do」塊。 – Wyzard

+0

對,這些是隱式嵌套的作用域,你可以通過在純代碼中明確地嵌套'let..in'和'where'塊來做同樣的事情。在最新的GHCi中,新手可能會感到困惑,因爲'let'關鍵字在綁定中不再需要,而且看起來像是在REPL中定期進行Haskell聲明。 –

0

通常不,不。通常,Haskell變量不可變。然而,有些庫包含可變變量。例如,Data.IORef包含IORef,這是可變的。我將在下面解釋如何使用它們,但作爲一般規則,使用我會推薦它,而不是以更多功能的方式編寫代碼。

隨着IOREF,你的函數typer可以改寫

typer :: IORef [Char] -> Char -> IO() 
typer left c = writeIORef left . (++[c]) =<< readIORef left 

讓我們看看它的不同部分。

  • readIORef leftIORef拉出值"hello"並把它封裝在IO單子。
  • f =<< x需要x,其被包裹在一個單子,解開它,並將其作爲一個參數傳遞給f,但只有如果我們保證f返回包裹在同一單子的值。這裏,x"hello"包裹在IOfwriteIORef left . (++[c])
  • .將功能writeIORef left(++[c])組合成一個,以便在writeIORef left之前應用(++[c])
  • (++[c])追加字符c
  • writeIORef left返回一個包含在IOIO())中的空動作,但其副作用是修改了IORefleft的值。由於它返回包含在IO中的值,因此我們對=<<所做的承諾已完成。

利用上述功能,代碼

main :: IO() 
main = do x <- newIORef "hello" 
      typer x 'a' 
      readIORef x >>= print 

應該產生所期望的效果。