В настоящее время я имею дело с функцией, которая выглядит следующим образом:
foo = (\(a:b:c:d:e:f:_) -> foobar a b c d e f) . (++ repeat def)
Другими словами, для данного списка он использует первые шесть элементов для чего-то, и если список имеет длину менее шести элементов, он использует def
в качестве замены для отсутствующих. Это полная сумма, но ее части не похожи map fromJust . filter isJust
, так что мне это не нравится. Я попытался переписать это так, чтобы он не нуждался в какой-либо пристрастности, и получил это:
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
Технически я сделал то, что хочу, но теперь это гигантский беспорядок. Как я могу сделать это более изящным и менее повторяющимся способом?
haskell
pattern-matching
Джозеф Сибл-Восстановить Монику
источник
источник
uncons :: Default a => [a] -> (a,[a])
который по умолчаниюdef
. Или дефолтtakeWithDef
. И / или шаблон просмотра / синоним шаблона. Это требует написания некоторого вспомогательного вспомогательного кода.case xs ++ repeat def of a:b:c:d:e:f:_ -> ...
достаточно локальный, чтобы я не задумывался о том, чтобы просто использовать его и пропустить все дополнительные механизмы, которые вводят существующие ответы. Обычно меня раздражают более глобальные аргументы совокупности (которые включают в себя инварианты, поддерживаемые, например, в нескольких вызовах функций).takeWithDef
не может использоваться, если он возвращает обычный список, так как нам нужно сопоставить с шаблоном, что: - / Правильное решение - то, что Даниэль написал ниже во втором ответе.uncons
только получает первый элемент, так что это не так полезно.Ответы:
Используя безопасный пакет, вы можете написать, например:
источник
Это как минимум короче:
Вы можете легко увидеть, что шаблоны являются исчерпывающими, но теперь вам нужно немного подумать, чтобы увидеть, что они всегда заканчиваются. Так что я не знаю, можете ли вы считать это улучшением.
В противном случае мы можем сделать это с помощью государственной монады, хотя она немного тяжеловесна:
Я также мог бы представить, используя бесконечный тип потока, как
потому что тогда вы могли бы построить
foo
изrepeat :: a -> S a
,prepend :: [a] -> S a -> S a
иtake6 :: S a -> (a,a,a,a,a,a)
, все из которых может быть полным. Вероятно, не стоит, если у вас нет такого типа под рукой.источник
data S a = a :- S a; infixr 5 :-
это выглядит довольно чисто;foo xs = case prepend xs (repeat def) of a:-b:-c:-d:-e:-f:-_ -> foobar a b c d e f
,Просто для развлечения (и не рекомендуется, это для приколов), вот еще один способ:
Тип, который вы используете в сопоставлении с образцом, означает передачу уровня типа, естественного для определения
takeDef
количества элементов, на которые нужно посмотреть.источник
foo (takeDef -> a:-b:-c:-d:-e:-f:-Nil) -> foobar a b c d e f
одной строкой. Остальное я не считаю, так как это код, который должен быть в какой-то библиотеке для повторного использования. Если он должен быть написан только для этого случая, это явно излишне, как вы говорите.