2015-04-14 31 views
0

我寫了一個簡單的函數現在哈斯克爾 - 改寫參數會導致意外行爲

someFunc list elem = do 
    list <- elem:elem:elem:list 
    return elem 

,當我使用它,我得到的輸出喜歡這個

*Main> someFunc [] 'a' 
"aaa" 

儘管事實上,這個功能沒有實際用途,爲什麼會發生?爲什麼編輯列表在elem中有效?以及如何爲list分配新值以避免這種情況?

回答

0

你是(顯然不小心)使用列表monad。在Python中,你的摘要將表示爲:

for list in [elem, elem, elem] + list: 
    yield elem 

也就是說,您創建一個包含一個elem列表中的每個項目,再加上三個列表。

這裏就是你可能打算:

someFunc list elem = do 
    list <- return (elem:elem:elem:list) 
    return elem 

這只是創建了一個新變量list陰影舊的一個,並且完全忽略它返回elem

2

您可以將新值不分配給list,發生的事情是在<-左側的list是比list<-權不同。如果您打開與-Wall警告你會看到

<interactive>:13:19: Warning: 
    This binding for `elem' shadows the existing binding 
     imported from `Prelude' (and originally defined in `GHC.List') 

<interactive>:14:7: Warning: 
    This binding for `list' shadows the existing binding 
     bound at <interactive>:13:14 

<interactive>:14:7: Warning: Defined but not used: `list' 

您沒有使用list <- ...定義的名稱list,只是定義它,這樣它發生影着現有綁定。

原因someFunc [] 'a'返回"aaa"是由於列表monad的工作原理。這做記號將相當於

someFunc list e = (e:e:e:list) >>= \l -> return e 

而對於名單,>>=基本上是concatMap,所以你必須

someFunc list e = concatMap (\l -> return e) (e:e:e:list) 

[]所以替代list'a'e我們得到

someFunc [] 'a' = concatMap (\l -> return 'a') "aaa" 
       = concat $ map (\l -> return 'a') "aaa" 
       = concat [['a'], ['a'], ['a']] 
       = ['a', 'a', 'a'] 
       = "aaa" 

您的困惑可能來自使用return。在大多數語言中,return是一個關鍵字,但在Haskell中它只是一個函數。它不提前退出函數調用,它所做的只是在您所在的monad環境中包裝一個值。對於列表return x = [x],這就是整個定義。另外,在Haskell中,你不能重新賦值,但你可以用新的定義來隱藏它們。不過,如果您總是使用-Wall-Werror進行編譯,您將不會遇到此問題。

4

請注意,您的功能將被取消加糖到這一點:

someFunc :: [b] -> b -> [b] 
someFunc list elem = (elem:elem:elem:list) >>= \list -> return elem 

現在注意到,在\list -> return elemlist爲你傳遞給函數的輸入list不同。

現在看到的名單單子實例定義:

instance Monad [] where 
    return x = [x] 
    xs >>= f = concat (map f xs) 
    fail _ = [] 

所以,你的代碼轉換爲這種形式最後:

someFunc list elem = concat $ map (\list -> return elem) (elem:elem:elem:list) 

現在你能理解爲什麼您獲得的輸出?

someFunc [] 'a'將得到應用這樣的:

concat $ map (\list -> return 'a') ('a':'a':'a':[]) 
concat $ [['a'],['a'],['a']] 
'a':'a':'a':[] 
"aaa" 
+0

的脫糖去'someFunc列表ELEM =(ELEM:ELEM:ELEM:列表)>> =(\列表 - >回ELEM)'; desugaring沒有引入新的可用'x'。 –

+0

@GPhilip謝謝,更新。我最初把它保存爲'x',以確保OP瞭解與輸入值不同的'list'。 – Sibi