Понимание списка Haskell Неисчерпывающий шаблон при вызове более одного параметра

Для начала я создал Type StudentMark, который является кортежем, во-первых, с использованием String, а затем с Int.

type StudentMark = (String, Int)

Это моя функция capMarks:

capMarks :: [StudentMark] -> [StudentMark]
capMarks [cMarks] = [(st, mk) | (st, mk) <- [capMark cMarks]]

А вот моя функция capMark:

capMark :: StudentMark -> StudentMark
capMark (st, mk)
    |   mk > 39   =   (st, 40)
    |   mk < 40   =   (st, mk)

Предполагается вернуть:

[("Jo", 37), ("Sam", 40)]

от:

capMarks [("Jo", 37), ("Sam", 76)]

Но верный и ожидаемый ответ вернется только тогда, когда я введу только 1 параметр в функцию, например:

capMarks [("Jake", 50)]

Или же

capMarks [("Jake"), 30]

Но использование двух (или более), как предполагается, просто скажет мне, что в функции capMarks есть неисчерпывающий шаблон.

2 ответа

Решение

Давайте проанализируем ваши capMarks функция:

capMarks :: [StudentMark] -> [StudentMark]
capMarks [cMarks] = [(st, mk) | (st, mk) <- [capMark cMarks]]

Прежде всего capMarks [cMarks] = ... это сопоставление с образцом. Это соответствует списку, который содержит один элемент. Я предполагаю, что вы хотите сделать что-то со всем списком, поэтому измените это на capMarks cMarks = ...

следующий ... [(st, mk) | (st, mk) <- [capMark cMarks]] будет применять capMark работать с единственным элементом в вашей исходной схеме сопоставления с образцом, а затем поместить результат как единственный элемент списка. Похоже, что вы хотите подать заявку capMark к каждому элементу списка. Поэтому, если мы последуем предыдущему предложению, вам нужно сделать что-то вроде ... [capMark mark | mark <- cMarks], Это в точности как указано ранее: применить capMark к каждому элементу cMarks список.

Окончательный вариант:

capMarks :: [StudentMark] -> [StudentMark]
capMarks cMarks = [capMark mark | mark <- cMarks]

Кроме того, вы также можете использовать сопоставление с образцом и явную рекурсию:

capMarks [] = []  
capMarks (x:xs) = capMark x : capMarks xs

Первая строка говорит, что capMarks применяется к пустому списку является пустым списком. Вторая строка говорит, что capMarks применяется к списку с хотя бы одним элементом capMark к первому элементу, а затем рекурсивно применить capMarks к остальной части списка.

Это такой распространенный паттерн в Haskell, что есть функция map это обобщает это. С помощью map невероятно просто:

capMarks cMarks = map capMark cMarks

map имеет тип (a -> b) -> [a] -> [b] это означает, что он берет функцию и список и возвращает список. (The a а также b просто скажите компилятору, какие типы должны быть одинаковыми.) map затем применяет функцию к каждому элементу в списке ввода.

Со временем вы узнаете о частичном применении функций и стиле без точек. С этими двумя понятиями версия, использующая map можно немного упростить:

capMarks = map capMark

Пока не беспокойтесь об этом. Я просто добавляю это здесь для полноты.

Вы должны проверить, как работает сопоставление с образцом в Haskell.

capMarks [x] будет соответствовать только список с одним элементом. То, что вы, вероятно, хотите, что-то вроде capMarks myList = [ ... | ... <- f myList] или определить остатки дел рекурсивным способом.

Например

capMarks [] = []
capMarks x:xs = capMark x : capMarks xs

Эта упрощенная "версия" работает в объятиях

capMarks :: [Integer] -> [Integer]
capMarks xs = [(*) 2 x | x <- xs]
Другие вопросы по тегам