2017-06-18 18 views
6

Tôi đang đăng một số mã dưới đây trong Haskell. Hãy xem mã như một ví dụ, mà tôi sẽ sử dụng, để giải thích những gì tôi muốn biết.Haskell: Áp dụng chức năng cho tất cả các kết hợp đối số

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 

Cuối cùng, checkIfCorrect trả về True chỉ cho một kết hợp các đối số. checkIfCorrect thực sự là chức năng dài, vì vậy tôi quyết định đăng thay thế tại đây. Trong ví dụ trên, chức năng checkIfCorrect được áp dụng (theo chức năng try) đến: đầu tiên [Char] trên danh sách đầu tiên, trước tiên [Char] trên danh sách thứ hai và [Char] một danh sách thứ ba đầu tiên. Nếu phương trình được bảo vệ đầu tiên không được thực hiện, hàm checkIfCorrect được áp dụng cho: số thứ hai [Char] trên danh sách đầu tiên ... v.v. Những gì tôi muốn đạt được, là để áp dụng chức năng checkIfCorrect (theo chức năng try) cho tất cả các kết hợp của [Char] s từ tất cả các danh sách ([[Char]]). Ý tôi là như sau (ví dụ): thứ ba [Char] trên danh sách đầu tiên, thứ tám [Char] trong danh sách thứ hai, thứ mười một [Char] trên danh sách thứ ba và cứ tiếp tục như vậy. Mọi người với mọi người. Làm thế nào tôi có thể dễ dàng đạt được điều đó?

+0

Bạn có thể muốn thay đổi 'try' để kiểu trả về là một bộ gồm 3 giá trị thay vì một danh sách. Bằng cách này, rõ ràng là bạn mong đợi chính xác 3 giá trị nằm trong danh sách. – 4castle

Trả lời

5

, bạn có thể làm cho nó cũng trông thanh lịch hơn với danh sách hiểu:

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 

Mã này hoạt động như sau: phần bên phải của danh sách hiểu bao gồm ra khỏi một "liệt kê "một phần (được biểu thị bằng phần bình luận). Kể từ khi chúng tôi viết a <- as, b <- bs, c <- cs nó có nghĩa là a sẽ mất bất kỳ giá trị từ as, và cho mỗi ví dụ a, b sẽ mất bất kỳ giá trị của bs, vv Vì vậy, đó có nghĩa là tất cả các kết hợp có thể sẽ được phát ra.

Tiếp theo đó là "lọc" giai đoạn: có một vị checkIfCorrect a b c sẽ được gọi và chỉ khi vị ngữ trả True, kết quả sẽ là "mang lại".

Ở bên trái, chúng tôi thấy "yield". Nó mô tả những gì cần thêm vào danh sách (dựa trên điều tra) cho bộ lọc thành công. Nếu điều đó xảy ra, chúng tôi thêm [a,b,c] vào danh sách đó. Nếu có nhiều cấu hình như vậy thành công, chúng tôi có thể kết thúc với danh sách chứa nhiều giải pháp. Tuy nhiên lưu ý rằng danh sách hiểu được thực hiện uể oải: do đó, miễn là bạn không yêu cầu ít nhất một yếu tố như vậy, nó sẽ không tạo ra các yếu tố đầu tiên, cũng không phải là thứ hai, vv

Bây giờ chúng ta cũng cần head (ở phía trước của danh sách hiểu). head :: [a] -> a trả về thành phần đầu tiên của danh sách. Vì vậy, try sẽ trả về phần tử đầu tiên thỏa mãn điều kiện.

11

Tôi chỉ muốn hiển thị cho bạn một cách khác để viết @WillemVanOnsem's code. Tôi đoán rằng bạn là một người mới bắt đầu Haskell, vì vậy hy vọng câu trả lời này sẽ cho bạn một cái nhìn thoáng qua về một ý tưởng phong phú và đẹp mà bạn sẽ sớm được học về đầy đủ khi bạn tiến bộ với ngôn ngữ.Vì vậy, đừng lo lắng quá nhiều nếu bạn không hiểu tất cả mọi thứ về mã này ngay lập tức; Tôi chỉ cố gắng để cho bạn một hương vị!

Một danh sách hiểu luôn có thể được thay đổi công sử dụng danh sách đơn nguyên:

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] 

Tôi đang sử dụng do ký hiệu như một ký hiệu đặc biệt cho vòng lồng nhau: đối với mỗi a trong as, đối với mỗi b trong bs và cho mỗi c trong cs, chúng tôi mang lại [a,b,c] nếu checkIfCorrect trả lại True. Bản dịch từ việc hiểu danh sách rất đơn giản: các phần "liệt kê" của danh sách hiểu được chuyển thành "liên kết" bằng cách sử dụng <-, "bộ lọc" chuyển thành các cuộc gọi thành guard và "lợi nhuận" được chuyển thành return s.

Trong một ngôn ngữ bắt buộc như Python bạn có thể viết nó như thế này:

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] 

Giống như chính trị dưới quyền bá chủ tự do mới của phương Tây, mã bắt buộc dần tiến bước rightward. "Cầu thang" mã như thế này thực sự cây trồng lên khá thường xuyên trong lập trình bắt buộc (nghĩ về "địa ngục gọi lại" trong JS), do đó, monads đã được phát minh để giúp chống lại xu hướng này. Chúng hóa ra hữu ích đến mức một cú pháp đặc biệt được phát minh cho chúng, cụ thể là do -notation.

+5

Đoạn cuối cùng được trích dẫn. :) – Alec

+0

'find checkIfCorrect (liftM3 (,,) là bs cs)' có lẽ sẽ là một cách khác để làm điều đó, nhưng chúng sẽ cần phải sửa đổi các loại chức năng của chúng một chút. – 4castle

+0

không phải 'yield', mà là' return', để trả về kết hợp * đầu tiên * vượt qua bài kiểm tra. –

3

Mặc dù cả hai câu trả lời của Willem Van Onsem và The Orgazoid đều tốt (upvoted), bạn cũng có thể tiếp cận một phần vấn đề theo cách tổng quát hơn và không chỉ cho các danh sách.

Đối với những điều sau đây, bạn sẽ cần những hàng nhập khẩu:

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

Nếu tôi hiểu câu hỏi một cách chính xác, bạn có muốn thử tất cả các kết hợp của as, bscs. Bạn thường có thể đạt được hành vi kết hợp giống như với các Applicative typeclass:

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

(,,) là một chức năng mà tạo ra ba (các bộ ba nguyên tố) từ ba giá trị cá nhân.

này hoạt động cho các danh sách, bởi vì danh sách là applicative:

*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)] 

nhưng nó cũng làm việc cho ví dụ Maybe s:

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

Với điều đó, bây giờ bạn có thể xác định cốt lõi của chức năng của bạn:

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] 

Bạn sẽ nhận thấy rằng chức năng helper này là hoàn toàn chung. Nó hoạt động với bất kỳ cá thể MonadPlus nào của bất kỳ phần tử nào có chứa a.

Dưới đây là một số ví dụ:

*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 

Bạn nên lưu ý rằng nếu predicate luôn trả False, bạn sẽ nhận được gì trở lại.Đối với danh sách, bạn sẽ nhận được danh sách trống; cho Maybe, nghĩa là bạn nhận được Nothing.

Cho đến nay tất cả đều chung chung, nhưng checkIfCorrect thì không. Nó cũng giống như bạn muốn chỉ nhận được các yếu tố đầu tiên phù hợp. Bạn có thể đạt được điều đó bằng cách soạn try' với checkIfCorrect:

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 

Ở đây, tôi đã tạo ra một isCorrect chức năng riêng để uncurry các checkIfCorrect chức năng. Sau đó tôi đã sử dụng kết hợp của listToMaybefromMaybe để trả lại phần tử đầu tiên của danh sách kết quả. Câu trả lời khác ở đây sử dụng head, nhưng điều đó sẽ ném một ngoại lệ nếu danh sách trống, vì vậy tôi đã sử dụng kết hợp này để thay thế, bởi vì nó an toàn.

+0

hoặc 'combination = liftA3 (,,) dưới dạng bs cs' –

Các vấn đề liên quan