2014-03-19 57 views
2

假設我們有一個預定義列表pairs,它是所有對(x,y)的列表,使得x,y∈{1..9}和2x = y。在Haskell它會是什麼樣子:爲什麼生成器會覆蓋Haskell列表理解中的變量值?

pairs = [ (x,y) | x <- [1..9], y <- [1..9], 2 * x == y ] 

現在,我想用pairs定義一個新的列表triplets,這是所有三胞胎(X,Y,Z),使得列表X,Y,z∈ {1..9},2x = y和2y = z。編寫它的明顯方法是:

triplets = [ (x,y,z) | (x,y) <- pairs, (y,z) <- pairs ] 

奇怪的是,這不起作用。

現在,我知道技術爲什麼不:該(y,z) <- pairs發生器環比(x,y) <- pairs更加緊密,和值將其分配給y重寫任何y是進入該循環之前。但爲什麼你會設計一種語言來做到這一點?它會不會更直觀(並且符合數學約定)讓發電機'看到'它的左邊並重新使用預先指定的值,以便每個變量在每次迭代中都有一個值?我認爲這個設計選擇必須有一個實用的理由,但我不確定它是什麼。

+0

你爲什麼不只是把它寫爲'三胞胎= [(X,Y,Z)| x < - [1..9],y < - [1..9],z < - [1..9]]?? –

+1

@AaditMShah具體來說,OP需要'(x,2 * x,4 * x)'的三元組,它應該返回'[(1,2,4),(2,4,8)]'。 – bheklilr

+0

在這種情況下直接定義更容易,但在使用預先存在的列表更可取的複雜情況下。爲了討論的目的,我只是選擇了一個簡單的例子。 – user3287108

回答

5

的問題是,列表理解僅僅是另一種方式來寫的名單單子做記號:

triplets = do 
    (x, y) <- pairs 
    (y, z) <- pairs 
    return (x, y, z) 

因此,相反,你看,你一個單子行動中重新綁定的名稱。雖然這是允許的,如果你打開-Wall,你得到的警告

Warning: Defined by not used: `y' 
Warning: 
    The binding for `y' shadows the existing binding bound at module:line:column 

相反,首選的方法是做

triplets = do 
    (x, y1) <- pairs 
    (y2, z) <- pairs 
    guard $ y1 == y2 
    return (x, y1, z) 

或者作爲一個修真

triplets = [(x, y1, z) | (x, y1) <- pairs, (y2, z) <- pairs, y1 == y2] 

記住咒語「明顯比隱含更好」。你不會因爲能夠完成你想要做的事情而獲得任何效率獎勵,而這清楚地表達了你的意圖,而[(x, y, z) | (x, y) <- pairs, (y, z) <- pairs]更難理解。

2

操作上可以做反正是某種過濾生成的最佳

[ (x, y, z) | (x, y) <- pairs, (y', z) <- pairs, y == y' ] 

[ (x, y, z) | (x, y) <- pairs, (_, z) <- filter (\(y', _) -> y == y') pairs ] 

這也是有益的思考如何將這些(straightfowardly)轉換成列表單子是使用

-- [ (x, y, z) | (x, y) <- pairs, (y', z) <- pairs, y == y' ] 

do (x, y) <- pairs 
    (y', z) <- pairs 
    guard (y == y') 
    return (x, y, z) 

這使得在第一個例子更加清晰

-- [ (x, y, z) | (x, y) <- pairs, (y, z) <- pairs ] 

do (x, y) <- pairs 
    (y, z) <- pairs -- shadows! 
    return (x, y, z) 

值得作爲一個明確表示,儘管這種直覺普遍認爲,列表內涵中顯式的Haskell報告定義,因而可能與語法超載奇怪交互的值陰影。

+0

在你的最後一個例子中,列表理解應該是'[(x,y',z)| (x,y)< - 對,(y',z)< - 對],因爲在do-notation中,'(y,z)< - '中的'y'覆蓋'(x ,y)< - '。 – bheklilr

+0

哦,我複製並粘貼了錯誤的列表理解 –

1

這樣做的原因設計選擇一定是一致性

g xs = [x | x <- xs, y <- xs] 

g :: [a] -> [a]型的,因爲它是現在,但

h xs = [x | x <- xs, x <- xs] 

將不得不h :: (Eq a) => [a] -> [a]型。

如果允許在列表解析中重複使用模式變量來表示相同,那麼也必須允許在函數中使用相同的約束,同樣的問題是突然出現約束。順便提一句,這樣的模式被稱爲非線性

比較接收到的參數的相等性也會改變它們的嚴格性(兩者都必須被強制)。由於Haskell的參照透明性,我們不能要求參數只是「相同」,因此「指針相等」沒有被定義(換句話說,在Haskell中不可能區分x = 1; z = g x xx = 1; z = g x 1)。另一方面,如果平等確實是必要的,那麼添加一個明確的平等保衛(如其他答案所示)是相當容易的。

還看到:

相關問題