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
, bs
và cs
. 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 listToMaybe
và fromMaybe
để 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.
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