Как совместное использование Haskell и сопоставление образцов? - программирование
Подтвердить что ты не робот

Как совместное использование Haskell и сопоставление образцов?

Я новичок в Haskell. Я понимаю, что функции начищены, чтобы стать функциями, которые принимают один параметр. Я не понимаю, как добиться соответствия шаблону нескольким значениям, если это так. Например:

Предположим, что мы имеем следующее полностью произвольное определение функции:

myFunc :: Int -> Int -> Int
myFunc 0 0 = 0
myFunc 1 1 = 1
myFunc x y = x `someoperation` y

Является ли частично примененная функция, возвращаемая myFunc 0 по существу:

partiallyAppliedMyFunc :: Int -> Int
partiallyAppliedMyFunc 0 = 0
partiallyAppliedMyFunc y = 0 `someoperation` y

Таким образом, удаление постороннего шаблона, который не может совпадать? Или... что здесь происходит?

4b9b3361

Ответ 1

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

myFunc = \x y -> case x of
    0 -> case y of
        0 -> 0
        _ -> x `someoperation` y
    1 -> case y of
        1 -> 1
        _ -> x `someoperation` y
    _ -> x `someoperation` y

Здесь вы можете увидеть множество субоптимальных вещей: термин someoperation многократно повторяется, и функция ожидает оба аргумента, прежде чем он даже начнет вообще делать case; см. Компилятор соответствия шаблону терминов, вдохновленный теорией конечных автоматов, чтобы обсудить, как вы могли бы улучшить это.

Во всяком случае, в этой форме на самом деле должно быть немного более ясно, как происходит шаг каррирования. Мы можем прямо заменить выражение x в этом выражении, чтобы посмотреть, что делает myFunc 0:

myFunc 0 = \y -> case 0 of
    0 -> case y of
        0 -> 0
        _ -> 0 `someoperation` y
    1 -> case y of
        1 -> 1
        _ -> 0 `someoperation` y
    _ -> 0 `someoperation` y

Так как это все еще лямбда, дальнейшее сокращение не производится. Вы можете надеяться, что достаточно умный компилятор может сделать немного больше, но GHC явно не делает больше; если вы хотите, чтобы больше вычислений выполнялось после подачи только одного аргумента, вы должны изменить свое определение. (Здесь есть компромисс между временем и пространством и правильным выбором слишком сложно сделать надежно. Поэтому GHC оставляет его в руках программиста, чтобы сделать этот выбор.) Например, вы можете явно писать

myFunc 0 = \y -> case y of
    0 -> 0
    _ -> 0 `someoperation` y
myFunc 1 = \y -> case y of
    1 -> 1
    _ -> 1 `someoperation` y
myFunc x = \y -> x `someoperation` y

а затем myFunc 0 уменьшится до гораздо меньшего выражения.