fuz*_*fuz 69 haskell variadic-functions function-parameter polyvariadic
我需要一个函数,它接受任意数量的参数(所有相同的类型),对它们做一些事情然后给出一个结果.在我的具体情况下,参数列表是不切实际的.
当我查看haskell库时,我看到函数printf(来自模块Text.Printf)使用了类似的技巧.不幸的是,通过查看来源我无法理解这种魔力.
有人可以解释如何实现这一点,或至少一些网页/纸/任何我可以找到一个良好的描述吗?
动机:
我需要它的原因非常简单.对于学校(计算机科学课),我们需要编写一个能够"记录"数学表达式,将其表示为字符串的模块(通过为自己的数据类型编写Num/Real/etc的实例),并执行对它的各种操作.
此数据类型包含变量的特殊构造函数,可以由值或指定函数的任何值替换.其中一个目标是编写一个函数,它使用一些变量(类型对(Char,Rational))来表达这种表达式并计算表达式的结果.我们应该看看如何最好地表达函数的目标.(我的想法:该函数返回另一个函数,它接受与函数中定义的变量一样多的参数 - 似乎是不可能的).
ken*_*ytm 104
关键点printf是能够返回String或函数.复制自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)
Run Code Online (Sandbox Code Playgroud)
我们可以提取的基本结构是
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)
Run Code Online (Sandbox Code Playgroud)
例如:
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
Run Code Online (Sandbox Code Playgroud)
然后我们可以使用
*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
Run Code Online (Sandbox Code Playgroud)
小智 9
很多人都在告诉你如何创建可变函数,但我认为在这种情况下你最好只使用类型[(Char,Rational)]的列表.
小智 9
KennyTM的答案很棒.下面是exec过程的示例,sumOf 1 4 7 10 :: Integer以便更好地说明.
sumOf 1 4 7 10
(( \ x -> ( sumOf . (x +) . toInteger ) 1 ) 4 7 10
((sumOf . (1 + ) . toInteger) 4 ) 7 10
( sumOf 5 ) 7 10
( sumOf . (5 + ) . toInteger ) 7 10
sumOf 12 10
sumOf . (12 + ) . toInteger 10
sumof 22
id 22
22
Run Code Online (Sandbox Code Playgroud)
我看了一下delnan引用的文章中链接的一个例子.在稍微盯着它之后,我想我终于理解了发生了什么:
它从这个类型类开始:
class BuildList a r | r-> a where
build' :: [a] -> a -> r
Run Code Online (Sandbox Code Playgroud)
管道(|)之后的那个位是功能依赖项.它表示所代表的类型a可以由表示的类型确定r.换句话说,您无法BuildList使用相同的r(返回类型)定义类型类的两个实例,但会有所不同a.
向前跳到build'实际使用函数的位置:
> build True :: [Bool]
Run Code Online (Sandbox Code Playgroud)
由于build只是build'使用空列表作为第一个参数调用,因此它与以下内容相同:
> build' [] True :: [Bool]
Run Code Online (Sandbox Code Playgroud)
在这个例子中,build'显然是返回一个列表.由于函数依赖,我们只能绑定到BuildList类型类的这个实例:
instance BuildList a [a] where
build' l x = reverse$ x:l
Run Code Online (Sandbox Code Playgroud)
非常直截了当.第二个例子更有趣.扩展定义build,它变成:
> build' [] True False :: [Bool]
Run Code Online (Sandbox Code Playgroud)
build'这种情况下的类型是什么?好吧,Haskell的优先规则意味着上面也可以这样编写:
> (build' [] True) False :: [Bool]
Run Code Online (Sandbox Code Playgroud)
现在很明显我们将两个参数传递给build'然后将该表达式的结果应用于值为'False'的参数.换句话说,表达式(build' [] True)应该返回类型的函数Bool -> [Bool].这将我们绑定到BuildList类型类的第二个实例:
instance BuildList a r => BuildList a (a->r) where
build' l x y = build'(x:l) y
Run Code Online (Sandbox Code Playgroud)
在此调用,l = []和x = True和y = False,这样的定义扩展到build' [True] False :: [Bool].那个签名绑定到第一个实例build',并且从那里开始它的位置相当明显.
| 归档时间: |
|
| 查看次数: |
10127 次 |
| 最近记录: |