Функция Haskell с другим количеством аргументов - PullRequest
0 голосов
/ 23 ноября 2018

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

{-# Language FlexibleInstances #-}

class Titles a where
  titleTeX ::  String -> a

instance Titles String where
  titleTeX str = titleWithFrame 1 "%" "%" "%" [str]

instance  Titles (String -> String) where
  titleTeX str = (\s -> titleWithFrame 1 "%" "%" "%" (s:[str]))

titleWithFrame::Int -> String -> String -> String -> [String] -> String
titleWithFrame nb beg end com lstr =
  cadr++cont++cadr
    where
          cadr = concat $ replicate nb (beg++rempl++end++"\n")
          cont = concatMap (\s -> beg++" "++s++" "++end++"\n") lstr
          rempl = take long $ concat $ replicate long com
          long = (maximum $ map length lstr) + 2

Когда я пытаюсь использовать эту функцию с помощью ghci, у меня получаются следующие результаты:

ghci> putStr $ titleTeX "Line 1"
%%%%%%%%%%
% Line 1 %
%%%%%%%%%%
ghci> putStr $ titleTeX "Line 1" "Line 2"
%%%%%%%%%%
% Line 1 %
% Line 2 %
%%%%%%%%%%
ghci> putStr $ titleTeX "Line 1" "Line 2" "Line 3"

<interactive>:4:10: error:
    • No instance for (Main.Titles ([Char] -> [Char] -> String))
        arising from a use of ‘titleTeX’
        (maybe you haven't applied a function to enough arguments?)
    • In the second argument of ‘($)’, namely
        ‘titleTeX "Line 1" "Line 2" "Line 3"’
      In the expression: putStr $ titleTeX "Line 1" "Line 2" "Line 3"
      In an equation for ‘it’:
          it = putStr $ titleTeX "Line 1" "Line 2" "Line 3"

Я не понимаю, где моя ошибка и почему моя поливариадная функция не работает с более чем 2 аргументами.

Знаете ли вы, откуда возникла моя ошибка?и Как заставить мою функцию работать с произвольным числом аргументов?

Ответы [ 2 ]

0 голосов
/ 24 ноября 2018

Ошибка возникает из-за того, что в вашей программе ровно два экземпляра Titles:

instance Titles String
instance Titles (String -> String)

Они позволяют вам вызывать titleTeX с одним и двумя аргументами соответственно, но для трех аргументов потребуется

instance Titles (String -> String -> String)

которого не существует.Или, как говорит GHC:

• No instance for (Main.Titles ([Char] -> [Char] -> String))
    arising from a use of ‘titleTeX’

([Char] - это то же самое, что String.)

Это как если бы вы определили функцию

foo :: [Int] -> Int
foo [x] = ...
foo [x, y] = ...

но foo [x, y, z] - ошибка.

Чтобы это работало для любого количества аргументов, нам нужно использовать рекурсию.Как и в случае со списками (где у вас обычно есть базовый регистр foo [] = ... и рекурсивный регистр foo (x : xs) = ..., который где-то вызывает foo xs), нам нужно определить экземпляр Titles в терминах других экземпляров:

instance Titles String
instance (Titles a) => Titles (String -> a)

Хитрость в том, что я не вижу способа реализовать titleTeX, который бы соответствовал вышеуказанным объявлениям.

Мне пришлось внести другие изменения в ваш код, чтобы он работал:

{-# Language FlexibleInstances #-}

titleTeX :: (Titles a) => String -> a
titleTeX str = titleTeXAccum [str]

titleTeX больше не метод.Это просто удобный интерфейс для фактического titleTeXAccum метода.

В принципе, мы могли бы опустить параметр String и определить titleTeX :: (Titles a) => a как titleTeX = titleTexAccum [], но тогда titleTex :: String вылетел бы ввремя выполнения (потому что мы в конечном итоге вызываем maximum для пустого списка).

class Titles a where
  titleTeXAccum :: [String] -> a

Наш метод теперь принимает список строк, которые он (каким-то образом) превращает в значение типа a.

instance Titles String where
  titleTeXAccum acc = titleWithFrame 1 "%" "%" "%" (reverse acc)

Реализация String проста: мы просто вызываем titleWithFrame.Мы также передаем reverse acc, так как порядок элементов в аккумуляторе обратный (см. Ниже).

instance (Titles a) => Titles (String -> a) where
  titleTeXAccum acc str = titleTeXAccum (str : acc)

Это важная часть: общий метод titleTeXAccum пересылает в другой метод titleTeXAccum (другого типа / другого экземпляра Titles).Это добавляет str к аккумулятору.Мы могли бы написать acc ++ [str], чтобы добавить новый элемент в конце, но это неэффективно: вызов titleTeXAccum с N элементами потребует O (N ^ 2) времени (из-за повторных обходов списка в ++).Использование : и единственный вызов reverse один раз в конце сокращают это до O (N).

0 голосов
/ 23 ноября 2018

Это работает, если вы используете функцию, которую вы представили, titleTeX, а не какую-то другую функцию, которую вы еще не показали, titleLaTeX.

...