2017-06-18 35 views
6

我下面張貼在Haskell一些代碼的所有組合。請將代碼作爲示例,我將使用它來解釋我想知道的內容。哈斯克爾:應用函數的參數

try :: [[Char]] -> [[Char]] -> [[Char]] -> [[Char]] 
try (a:as) (b:bs) (c:cs) | ((checkIfCorrect a b c) == True) = a:b:[c] 
          | otherwise = try as bs cs 

checkIfCorrect :: [Char] -> [Char] -> [Char] -> Bool 
checkIfCorrect a b c = True 

最終,checkIfCorrect回報True只有一個的參數組合。 checkIfCorrect確實是很長的功能,所以我決定在這裏發佈替代品。在上面的例子中,功能checkIfCorrect被applicated(由函數try),用於:第一[Char]所述第一列表上,所述第二列表上第一[Char]和第一[Char]一個第三列表。如果第一把守方程不滿足,功能checkIfCorrect適用於:第二[Char]第一列表...等等。我想伸手,是從所有列表 ([[Char]])applicate功能checkIfCorrect(按功能try)至[Char]一切都組合。我的意思是以下的(例如):所述第一列表上第三[Char],所述第二列表上第八[Char],第十一[Char]第三列表上等等。每個人與每個人。我怎麼能輕鬆達成?

+0

您可能需要更改'try',以便返回類型是3值的元組而不是列表。通過這種方式很顯然,您期望列表中只有3個值。 – 4castle

回答

5

,你可以把它也期待更多地飄逸着列表理解

try :: [[Char]] -> [[Char]] -> [[Char]] -> [[Char]] 
try as bs cs = head [ [a,b,c] | a <- as, b <- bs, c <- cs, checkIfCorrect a b c ] 
--     \__ __/ \__________ ____________/ \__________ _______/ 
--      v     v       v 
--      yield   enumeration     filter 

代碼的工作原理如下:列表理解的右半部分包括了一個「枚舉「部分(由註釋部分表示)。因爲我們寫a <- as, b <- bs, c <- cs這意味着a將採取任何值從as,併爲每這樣ab將採取bs等任何價值因此,這意味着每一個可能的組合將被髮出。

接下來是「過濾」階段:有一個謂語checkIfCorrect a b c將被調用且僅當謂詞返回True,結果將是「得到」。

在左側,我們看到「收益率」。它描述了在過濾成功的情況下添加到列表(基於枚舉)的內容。如果發生這種情況,我們將[a,b,c]添加到該列表中。如果有多個這樣的配置成功,我們可能會得到一個包含多個解決方案的列表。但是請注意列表理解做懶洋洋地:所以只要你不要求至少一個這樣的元素,它不會產生第一要素,也不是第二等

現在,我們還需要head(在列表理解的前面)。 head :: [a] -> a返回列表的第一元素。因此try將返回滿足條件的第一個元素。

11

我只是想告訴你一種替代寫作方式@WillemVanOnsem's code。我猜你是Haskell初學者,所以希望這個答案能讓你一瞥一個豐富而美麗的想法,當你用語言進步的時候,你很快就會了解到這些想法。所以,如果你不能立即理解這些代碼,請不要太擔心。我只是想給你一個口味!

import Control.Monad (guard) 

try as bs cs = head $ do 
    a <- as 
    b <- bs 
    c <- cs 
    guard $ checkIfCorrect a b c 
    return [a,b,c] 

我使用do符號作爲嵌套循環一個特殊的符號:

列表解析可以隨時使用列表單子被重新用於as每個a,每個bbs ,並且對於cs中的每個c,如果checkIfCorrect返回True,則產生。列表解析的翻譯很簡單:列表理解中的「列舉」部分使用<-變成「綁定」,「過濾器」變成調用guard,「yield」變成returns。

在像Python命令式語言中你可能寫這樣的:

def try(as, bs, cs): 
    for a in as: 
     for b in bs: 
      for c in cs: 
       if checkIfCorrect(a, b, c): 
        yield [a,b,c] 

像西方新自由主義霸權下的政治,勢在必行碼逐漸向右遊行。像這樣的「階梯」代碼實際上在命令式編程中經常出現(想想JS中的「回調地獄」),所以發明了monad來幫助抵制這種趨勢。事實證明,它們非常有用,爲它們創造了一種特殊的語法,即do -notation。

+5

最後一段是可引用的。 :) – Alec

+0

'find checkIfCorrect(liftM3(,,)as bs cs)'也許是另一種方式來做到這一點,但他們需要稍微修改它們的函數類型。 – 4castle

+0

不是'yield',而是'return',只返回通過測試的* first *組合。 –

3

雖然Willem Van Onsem和The Orgazoid的答案都很好(upvoted),但您也可以採用更通用的方式處理部分問題,而不僅僅是列表。

對於下面,你會需要這些進口:

import Control.Monad (MonadPlus, mfilter) 
import Data.Maybe (fromMaybe, listToMaybe) 

如果我理解正確的問題,你想嘗試的asbs,並cs所有組合。通常可以實現組合樣行爲與Applicative類型類:

combinations = (,,) <$> as <*> bs <*> cs 

(,,)是創建三個單獨的值三元組(三元素的元組)的功能。

這適用於列表,因爲列表應用性:

*Prelude> (,,) <$> [1,2] <*> ["foo", "bar"] <*> [True, False] 
[(1,"foo",True),(1,"foo",False),(1,"bar",True),(1,"bar",False),(2,"foo",True),(2,"foo",False),(2,"bar",True),(2,"bar",False)] 

,但它也適用於例如Maybe S:

*Prelude> (,,) <$> Just 1 <*> Just "foo" <*> Just False 
Just (1,"foo",False) 

有了這一點,你現在可以定義你的函數的核心:

try' :: MonadPlus m => ((a, a, a) -> Bool) -> m a -> m a -> m a -> m [a] 
try' predicate as bs cs = 
    tripleToList <$> mfilter predicate combinations 
    where 
    combinations = (,,) <$> as <*> bs <*> cs 
    tripleToList (a, b, c) = [a, b, c] 

你會發現,這個輔助功能是完全通用的。它適用於任何包含元素a的任何MonadPlus實例。

下面是一些例子:

*Answer> try' (const True) ["foo", "bar", "baz"] ["qux", "quux", "quuz", "corge"] ["grault", "garply"] 
[["foo","qux","grault"],["foo","qux","garply"],["foo","quux","grault"],["foo","quux","garply"],["foo","quuz","grault"],["foo","quuz","garply"],["foo","corge","grault"],["foo","corge","garply"],["bar","qux","grault"],["bar","qux","garply"],["bar","quux","grault"],["bar","quux","garply"],["bar","quuz","grault"],["bar","quuz","garply"],["bar","corge","grault"],["bar","corge","garply"],["baz","qux","grault"],["baz","qux","garply"],["baz","quux","grault"],["baz","quux","garply"],["baz","quuz","grault"],["baz","quuz","garply"],["baz","corge","grault"],["baz","corge","garply"]] 
*Answer> try' (const False) ["foo", "bar", "baz"] ["qux", "quux", "quuz", "corge"] ["grault", "garply"] 
[] 
*Answer> try' (const True) (Just "foo") (Just "bar") (Just "baz") 
Just ["foo","bar","baz"] 
*Answer> try' (const False) (Just "foo") (Just "bar") (Just "baz") 
Nothing 

你應該注意到,如果predicate總是返回False,你會得到任何回報。對於列表,你會得到空的列表;對於Maybe,您從字面上得到Nothing

到目前爲止它都是通用的,但是checkIfCorrect不是。它也看起來像你只想得到匹配的第一個元素。你可以做到這一點通過checkIfCorrect組成try'

try :: [String] -> [String] -> [String] -> [String] 
try as bs cs = fromMaybe [] $ listToMaybe $ try' isCorrect as bs cs 
    where isCorrect (a, b, c) = checkIfCorrect a b c 

這裏,我以uncurry的checkIfCorrect函數創建一個私有isCorrect功能。然後,我使用listToMaybefromMaybe的組合返回結果列表的第一個元素。這裏的其他答案使用head,但如果列表爲空,那麼將會拋出異常,所以我使用了這種組合,因爲它很安全。

+0

或'combination = liftA3(,,)as bs cs' –