Obecnie mam do czynienia z funkcją, która wygląda następująco:
foo = (\(a:b:c:d:e:f:_) -> foobar a b c d e f) . (++ repeat def)
Innymi słowy, biorąc pod uwagę listę, używa pierwszych sześciu elementów do czegoś, a jeśli lista ma mniej niż sześć elementów, używa def
jako stand-in dla brakujących. To jest suma, ale kawałki nie są (tak jak map fromJust . filter isJust
), więc nie lubię tego. Próbowałem przepisać to, aby nie trzeba było używać żadnej stronniczości, i otrzymałem to:
foo [] = foobar def def def def def def
foo [a] = foobar a def def def def def
foo [a,b] = foobar a b def def def def
foo [a,b,c] = foobar a b c def def def
foo [a,b,c,d] = foobar a b c d def def
foo [a,b,c,d,e] = foobar a b c d e def
foo (a:b:c:d:e:f:_) = foobar a b c d e f
Technicznie zrobiłem to, co chciałem, ale teraz jest to gigantyczny bałagan. Jak mogę to zrobić w bardziej elegancki i mniej powtarzalny sposób?
haskell
pattern-matching
Joseph Sible-Reinstate Monica
źródło
źródło
uncons :: Default a => [a] -> (a,[a])
domyślną wartośćdef
. Lub domyślnietakeWithDef
. I / lub synonim wzorca widoku / wzorca. Wymaga to jednak napisania pomocniczego kodu pomocnika.case xs ++ repeat def of a:b:c:d:e:f:_ -> ...
jest na tyle lokalny, że nie zastanawiałbym się dwa razy po prostu o używaniu go i pomijaniu wszystkich dodatkowych mechanizmów, które wprowadzają istniejące odpowiedzi. Na ogół denerwują mnie bardziej globalne argumenty totalności (które obejmują niezmienniki utrzymywane w wielu wywołaniach funkcji, np.).takeWithDef
nie nadaje się do użycia, jeśli zwraca regularną listę, ponieważ musimy dopasować wzór, że: - / Właściwym rozwiązaniem jest to, co Daniel napisał poniżej w swojej drugiej odpowiedzi.uncons
dostaje tylko pierwszy element, więc nie jest to tak przydatne.Odpowiedzi:
Korzystając z bezpiecznego pakietu, możesz na przykład napisać:
źródło
Jest to co najmniej krótszy:
Możesz łatwo zobaczyć, że wzorce są wyczerpujące, ale teraz musisz pomyśleć trochę, aby zobaczyć, że zawsze się kończy. Więc nie wiem, czy możesz to uznać za poprawę.
W przeciwnym razie możemy to zrobić za pomocą monady państwowej, chociaż jest to nieco ciężkie:
Mógłbym również wyobrazić sobie użycie nieskończonego typu strumienia jak
bo wtedy można skonstruować
foo
zrepeat :: a -> S a
,prepend :: [a] -> S a -> S a
itake6 :: S a -> (a,a,a,a,a,a)
, z których każdy może być całkowite. Prawdopodobnie nie warto, jeśli nie masz już takiego typu pod ręką.źródło
data S a = a :- S a; infixr 5 :-
wygląda to całkiem czysto;foo xs = case prepend xs (repeat def) of a:-b:-c:-d:-e:-f:-_ -> foobar a b c d e f
.Dla zabawy (i niezalecane, to dla miłośników gier), oto inny sposób:
Typ użyty w dopasowaniu wzorca sprowadza się do przekazania naturalnego poziomu typu do
takeDef
powiedzenia, ile elementów należy obejrzeć.źródło
foo (takeDef -> a:-b:-c:-d:-e:-f:-Nil) -> foobar a b c d e f
jako jedną linię. Reszty nie liczę, ponieważ jest to kod, który powinien znajdować się w bibliotece do ponownego użycia. Jeśli trzeba to napisać tylko w tym przypadku, to, jak mówisz, wyraźnie przesada.