Chociaż zarówno odpowiedzi Willema Van Onsem, jak i The Orgazoid są dobre (awansowane), można również podejść do części problemu w bardziej ogólny sposób, a nie tylko w przypadku list.
dla następujących, będziesz potrzebował tych importu:
import Control.Monad (MonadPlus, mfilter)
import Data.Maybe (fromMaybe, listToMaybe)
Jeśli mam zrozumienia pytanie poprawnie, chcesz wypróbować wszystkie kombinacje as
, bs
i cs
. zazwyczaj można osiągnąć podobny zachowanie skojarzone z Applicative
typeclass:
combinations = (,,) <$> as <*> bs <*> cs
(,,)
to funkcja, która tworzy trójek (krotki trzech elementów) z trzech pojedynczych wartości.
To działa na listach, bo listy są aplikacyjnych:
*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)]
ale działa także na przykład Maybe
s:
*Prelude> (,,) <$> Just 1 <*> Just "foo" <*> Just False
Just (1,"foo",False)
Z tym, można teraz określić istotę swojej funkcji:
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]
zauważysz, że ta funkcja jest całkowicie generic pomocnika. Działa dla dowolnej instancji MonadPlus
dowolnego zawartego elementu a
.
Oto kilka przykładów:
*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
Należy zauważyć, że jeśli predicate
zawsze zwraca False
, dostaniesz nic z powrotem.Dla list otrzymasz pustą listę; dla Maybe
dosłownie dostajesz Nothing
.
Do tej pory wszystko jest ogólne, ale checkIfCorrect
nie jest. Wygląda na to, że chcesz uzyskać tylko pierwsze pasujące elementy. Można to osiągnąć poprzez komponowanie try'
z 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
Tutaj Utworzyłem prywatną isCorrect
funkcji w celu uncurry funkcję checkIfCorrect
. Następnie użyłem kombinacji listToMaybe
i fromMaybe
, aby zwrócić pierwszy element wynikowej listy. Inne odpowiedzi tutaj używają head
, ale to rzuci wyjątek, jeśli lista jest pusta, więc użyłem tego połączenia, ponieważ jest to bezpieczne.
Możesz chcieć zmienić 'try', aby typ zwracany był krotką 3 wartości zamiast listy. W ten sposób jest jasne, że oczekujesz dokładnie 3 wartości na liście. – 4castle