Haskell 参数个数不同的函数
Haskell function with different number of argument
我正在尝试创建一个带有 class 的 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 个参数。
你知道我的错误是从哪里来的吗?以及如何使我的函数使用任意数量的参数?
如果您使用您提供的功能,titleTeX
,而不是您尚未展示的其他功能,titleLaTeX
。
发生错误是因为您的程序中恰好有两个 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 xs
的递归案例 foo (x : 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]
以在末尾添加新元素,但这样效率很低:使用 N 个元素调用 titleTeXAccum
将花费 O(N^2) 时间(由于 [=46 中的重复列表遍历) =]).使用 :
并在最后仅调用一次 reverse
将其减少到 O(N)。
我正在尝试创建一个带有 class 的 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 个参数。
你知道我的错误是从哪里来的吗?以及如何使我的函数使用任意数量的参数?
如果您使用您提供的功能,titleTeX
,而不是您尚未展示的其他功能,titleLaTeX
。
发生错误是因为您的程序中恰好有两个 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 xs
的递归案例 foo (x : 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]
以在末尾添加新元素,但这样效率很低:使用 N 个元素调用 titleTeXAccum
将花费 O(N^2) 时间(由于 [=46 中的重复列表遍历) =]).使用 :
并在最后仅调用一次 reverse
将其减少到 O(N)。