代码之家  ›  专栏  ›  技术社区  ›  JeanJouX

具有不同参数个数的haskell函数

  •  1
  • JeanJouX  · 技术社区  · 6 年前

    我正在尝试用一个类创建一个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 回复  |  直到 6 年前
        1
  •  1
  •   melpomene    6 年前

    发生错误的原因是您只有两个 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 我们需要定义一个 标题 其他实例中的实例:

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

    棘手的一点是我看不到实现 蒂特莱克斯 这符合上述声明。

    我必须对您的代码进行其他更改才能使其工作:

    {-# Language FlexibleInstances #-}
    
    titleTeX :: (Titles a) => String -> a
    titleTeX str = titleTeXAccum [str]
    

    蒂特莱克斯 不再是一种方法了。它只是一个方便的前端 titleTeXAccum 方法。

    原则上我们可以忽略 参数和定义 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)
    

    实施 很简单:我们只是打电话 titleWithFrame .我们也通过了 reverse acc 因为蓄能器中的元件顺序是向后的(见下文)。

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

    这是关键部分:将军 金龟子 方法转发到另一个 金龟子 方法(不同类型/不同 标题 实例)。它补充说 str 到蓄能器。我们可以写 acc ++ [str] 在末尾添加新元素,但效率很低:调用 抽搐 使用n个元素需要O(n^2)时间(由于在 ++ )使用 : 只有打电话 reverse 最后一次,把这个减少到O(n)。

        2
  •  2
  •   Thomas M. DuBuisson    6 年前

    如果你使用你所展示的功能,它就会起作用, titleTeX 而不是你要展示的其他功能, titleLaTeX .

    推荐文章