Как создать поливариадную функцию haskell?

Мне нужна функция, которая принимает произвольное количество аргументов (все одного типа), что-то с ними делает и впоследствии возвращает результат. Список аргументов неосуществим в моем конкретном случае.

Когда я просматривал библиотеки haskell, я увидел, что функция printf (из модуля Text.Printf ) использует аналогичный обмануть. К сожалению, я не мог понять эту магию, посмотрев на источник.

Может кто-нибудь объяснить, как этого добиться, или, по крайней мере, какую-нибудь веб-страницу / бумагу / где-нибудь, где я мог бы найти хорошее описание для этого?

Мотивация:

Причина, по которой мне это нужно, действительно довольно проста. Для школы (урок информатики) мы должны написать модуль, который может «записывать» математическое выражение, выражать его в виде строки (посредством записи экземпляра Num / Real / и т. Д. Для собственного типа данных) и выполнять различные операции над ним.

Этот тип данных содержит специальный конструктор для переменной, который может быть заменен значением или чем-то еще указанной функцией. Одна из целей - написать функцию, которая принимает такое выражение с некоторым количеством переменных (пары типа (Char, Rational) ) и вычисляет результат выражения. Мы должны посмотреть, как лучше выразить цель функции. (Моя идея: Функция возвращает другую функцию, которая принимает ровно столько аргументов, сколько переменных, определенных в функции, - кажется невозможной).

69
задан Nicholas Montaño 15 August 2015 в 10:48
поделиться

4 ответа

Ключевыми моментами printf является возможность возвращать либо строку, либо функцию. Скопировано из http://www.haskell.org/ghc/docs/6.12.2/html/libraries/base-4.2.0.1/src/Text-Printf.html,

printf :: (PrintfType r) => String -> r
printf fmts = spr fmts []

class PrintfType t where
    spr :: String -> [UPrintf] -> t

instance (IsChar c) => PrintfType [c] where
    spr fmts args = map fromChar (uprintf fmts (reverse args))

instance (PrintfArg a, PrintfType r) => PrintfType (a -> r) where
    spr fmts args = \a -> spr fmts (toUPrintf a : args)

и основная структура, которую мы можем извлечь, это

variadicFunction :: VariadicReturnClass r => RequiredArgs -> r
variadicFunction reqArgs = variadicImpl reqArgs mempty

class VariadicReturnClass r where
   variadicImpl :: RequiredArgs -> AccumulatingType -> r

instance VariadicReturnClass ActualReturnType where
   variadicImpl reqArgs acc = constructActualResult reqArgs acc

instance (ArgClass a, VariadicReturnClass r) => VariadicReturnClass (a -> r) where
   variadicImpl reqArgs acc = \a -> variadicImpl reqArgs (specialize a `mappend` acc)

Например:

class SumRes r where 
    sumOf :: Integer -> r

instance SumRes Integer where
    sumOf = id

instance (Integral a, SumRes r) => SumRes (a -> r) where
    sumOf x = sumOf . (x +) . toInteger

тогда мы могли бы использовать

*Main> sumOf 1 :: Integer
1
*Main> sumOf 1 4 7 10 :: Integer
22
*Main> sumOf 1 4 7 10 0 0  :: Integer
22
*Main> sumOf 1 4 7 10 2 5 8 22 :: Integer
59
104
ответ дан 24 November 2019 в 13:47
поделиться

Я просмотрел пример , ссылка на который содержится в статье, на которую ссылается Делнан. Посмотрев на него немного, я думаю, что наконец понял, что происходит:

Он начинается с этого типа class:

class BuildList a r  | r-> a where
    build' :: [a] -> a -> r

Этот бит после вертикальной черты (|) является функциональной зависимостью. В нем говорится, что тип, представленный a , может быть определен типом, представленным r . Другими словами, вы не можете определить два экземпляра класса типов BuildList с одним и тем же r (возвращаемый тип), но разными a .

Немного забегая вперед, где фактически используется функция build ':

> build True :: [Bool]

Поскольку build просто вызывает build' с пустым списком как первый параметр, он такой же, как:

> build' [] True :: [Bool]

В этом примере build ' явно возвращает список. Из-за функциональной зависимости мы можем привязаться только к этому экземпляру класса типа BuildList :

instance BuildList a [a] where
    build' l x = reverse$ x:l

Довольно просто. Второй пример более интересен. Расширяя определение build , оно становится следующим:

> build' [] True False :: [Bool]

Какой тип build ' в данном случае? Что ж, правила приоритета Haskell означают, что вышеприведенное можно также записать так:

> (build' [] True) False :: [Bool]

Теперь становится ясно, что мы передаем два параметра в build ', а затем применяем результат этого выражения к параметр со значением False. Другими словами, ожидается, что выражение (build '[] True) вернет функцию типа Bool -> [Bool] . И это связывает нас со вторым экземпляром класса типов BuildList :

instance BuildList a r => BuildList a (a->r) where
    build' l x y = build'(x:l) y

В этом вызове l = [] и x = True и y = False , поэтому определение расширяется до build '[True] False :: [Bool] . Эта подпись привязывается к первому экземпляру build ', и довольно очевидно, куда она оттуда идет.

6
ответ дан 24 November 2019 в 13:47
поделиться

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

7
ответ дан 24 November 2019 в 13:47
поделиться

Многие люди говорят вам, как создавать вариативные функции, но я думаю, что в этом случае вам на самом деле лучше просто использовать список типа [(Char, Rational)].

9
ответ дан 24 November 2019 в 13:47
поделиться
Другие вопросы по тегам:

Похожие вопросы: