Как соответствовать остальной части списка, независимо от длины - PullRequest
1 голос
/ 17 июня 2019

Я хочу написать функцию, которая принимает list из String с сопоставлением с образцом.Например, я проверяю ключевое слово set следующим образом:

parseArgs :: [String] -> Either String Command
parseArgs ["set", k, v] = Right $ Command k v
parseArgs ["set", _] = Left "To few arguments for set"
parseArgs ["set", _, _, _] = Left "To much arguments for set"

Как вы видите, я хочу соответствовать parseArgs ["set", "key", "value", ...].Но текущий подход дает мне совпадение только на ["set", "key", "value", "something"], а не на других записях.

Как бы мне этого добиться?

1 Ответ

2 голосов
/ 17 июня 2019

Вы можете использовать конструкцию списка " cons ":

parseArgs :: [String] -> Either String Command
parseArgs ["set", k, v] = Right $ Command k v
parseArgs ["set", _] = Left "To few arguments for set"
parseArgs <b>("set": _: _: _)</b> = Left "To much arguments for set"

("set": _: _: _) является компактной формой ("set": (_: (_: _))), поэтому мы сопоставляем здесь список, который не пуст, и где хвост не пуст, и где этот хвост не пуст, поэтому список, который содержит как минимум три элемента.

На самом деле ["set", _, _, _] это просто синтаксический сахар для ("set": (_: (_: (_:[])))). Поэтому, используя подстановочный знак _ вместо пустого списка [] в хвосте, мы оставляем его открытым, что следует далее.

Вы забыли случай, когда просто пропустили ["set"], мы также можем использовать ту же технику для этого:

parseArgs :: [String] -> Either String Command
parseArgs ["set", k, v] = Right $ Command k v
parseArgs ("set": _: _: _) = Left "To much arguments for set"
parseArgs <b>("set":_)</b> = Left "To few arguments for set"

Таким образом, это будет соответствовать одноэлементному списку и списку с двумя элементами, где каждый раз первый элемент будет "set".

Мы можем решить параметризовать проверку длины, например:

checkLength :: Int -> [a] -> Either String b
checkLength n _ | n < 0 = Left "Too much arguments"
checkLength n [] = Left "Too few arguments"
checkLength n (_:xs) = checkLength (n-1) xs

или даже проще с drop:

checkLength :: Int -> [a] -> Either String b
checkLength n = Left . foldr (const . const "Too much") "Too few" . drop n 

Тогда мы можем проверить это как:

parseArgs :: [String] -> Either String Command
parseArgs ["set", k, v] = Right $ Command k v
parseArgs ("set": xs) = checkLength 2 xs
...