如何创建多变量 Haskell 函数?
我需要一个函数,它接受任意数量的参数(全部相同类型),对它们执行某些操作,然后返回结果。在我的具体情况下,列出参数是不切实际的。
当我查看 haskell 库时,我发现函数 printf(来自模块 Text.Printf)使用了类似的技巧。不幸的是,我无法通过查看源代码来理解这种魔力。
有人可以解释如何实现这一点,或者至少在一些网页/论文/任何我可以找到对此的良好描述的地方吗?
动机:
我需要这个的原因非常简单。对于学校(计算机科学课),我们需要编写一个能够“记录”数学表达式的模块,将其表达为字符串(通过为自己的数据类型编写 Num/Real/etc 的实例),并执行对其进行各种操作。
此数据类型包含变量的特殊构造函数,可以用指定函数的值或其他内容替换该变量。目标之一是编写一个函数,该函数采用带有一定数量变量((Char,Rational)
类型对)的表达式并计算表达式的结果。我们应该看看如何最好地表达函数的目标。 (我的想法:该函数返回另一个函数,该函数接受与函数中定义的变量完全相同的参数 - 似乎是不可能的)。
I need a function which takes an arbitrary number of arguments (All of the same type), does something with them and afterwards gives a result back. A list of arguments is impracticable in my specific case.
As I looked through the haskell libs, I saw that the function printf
(from module Text.Printf
) uses a similar trick. Unfortunately, I couldn't understand that magic by looking at the source.
Can somebody explain how to achieve this, or at least some webpage/paper/whatever where I could find a good description for this?
Motivation:
The reason I need this is really quite simple. For school (computer science class), we are required to write a module that is able to "record" a mathematical expression, express it as a string (Via writing an instance of Num/Real/etc for an own datatype), and perform various operations on it.
This datatype contains a special constructor for a variable, which may be replaced by a value or whatever by a specified function. One of the goals is to write a function, which takes such an expression with some number of variables (pairs of type (Char,Rational)
) and calculates the result of the expression. We should look at how to express the goal of the function best. (My idea: The function returns another function which takes exactly as many arguments as vars that are defined in the function - seems to be impossible).
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
KennyTM 的回答很棒。下面以
sumOf 1 4 7 10 :: Integer
的执行过程为例来更好地说明。KennyTM's answer is great. Below is an example of the exec process of
sumOf 1 4 7 10 :: Integer
to give a better illustration.很多人告诉你如何创建可变参数函数,但我认为在这种情况下,你实际上最好只使用 [(Char,Rational)] 类型的列表。
Lots of people are telling you how to create variadic functions, but I think in this case you're actually better off just using a list of type [(Char,Rational)].
在有关可变参数函数的 wiki 文章中,引用了这篇文章。我想这就是 printf 所做的,但我也不明白。无论如何,这肯定是一种矫枉过正,特别是因为你的论点都是同一类型。只需将它们全部放在一个列表中即可。这就是列表的好处——任意数量的相同类型的事物。好吧,它不是很漂亮,但它不会比完整的多变量函数更难看。
In the wiki article on variadic functions, this article was referenced. I suppose this is what printf does, but I don't understand it either. Anyway, this is certainly an overkill, especially since your arguments are all of the same type. Just put them all in one list. That's what lists are good for - an arbitary number of things of the same type. Fine, it's not very beautiful, but it will hardly be uglier than a complete polyvariadic function.
我查看了 delnan 引用的文章中链接的示例。盯着它看了一会儿之后,我想我终于明白了发生了什么:
它从这个类型类开始:
管道(|)后面的那一位是函数依赖。它表示可以通过
r
表示的类型来确定a
表示的类型。换句话说,您无法定义具有相同r
(返回类型)但不同a
的BuildList
类型类的两个实例。跳转到
build'
函数实际使用的地方:因为
build
只是调用build'
并将空列表作为第一个参数,这与:在此示例中,
build'
显然返回一个列表。由于功能依赖性,我们只能绑定到BuildList
类型类的此实例:非常简单。第二个例子更有趣。扩展
build
的定义,它变成:在这种情况下
build'
的类型是什么?好吧,Haskell 的优先规则意味着上面的内容也可以这样写:现在很明显,我们将两个参数传递给
build'
,然后将该表达式的结果应用到一个参数值为“假”。换句话说,表达式(build' [] True)
预计会返回Bool -> 类型的函数。 [布尔]
。这将我们绑定到BuildList
类型类的第二个实例:在此调用中,
l = []
和x = True
和y = False
,因此定义扩展为build' [True] False :: [Bool]
。该签名绑定到build'
的第一个实例,并且从那里开始的走向相当明显。I took a look at an example linked from the article that delnan referenced. After staring at it for a bit, I think I finally comprehend what is going on:
It starts with this type class:
That bit after the pipe (|) is a functional dependency. It says that the type represented by
a
can be determined by the type represented byr
. In other words, you are prevented from defining two instances of theBuildList
typeclass with the samer
(return type), but differenta
.Jumping ahead a bit to where the
build'
function is actually used:Since
build
is just callingbuild'
with an empty list as the first parameter, this is the same as:In this example,
build'
is clearly returning a list. Because of the functional dependency, we can only be binding to this instance of theBuildList
type class:Pretty straightforward. The second example is more interesting. Expanding the definition of
build
, it becomes:What's the type of
build'
in this case? Well, the precedence rules of Haskell mean that the above could also be written like this:Now it becomes clear that we are passing two parameters to
build'
and then applying the result of that expression to a parameter with value 'False'. In other words, the expression(build' [] True)
is expected to return a function of typeBool -> [Bool]
. And that binds us to the second instance of theBuildList
typeclass:In this invocation,
l = []
andx = True
andy = False
, so the definition expands tobuild' [True] False :: [Bool]
. That signature binds to the first instance ofbuild'
, and it's fairly obvious where it goes from there.printf 的关键点是能够返回字符串或函数。复制自 http ://www.haskell.org/ghc/docs/6.12.2/html/libraries/base-4.2.0.1/src/Text-Printf.html,
我们可以提取出的基本结构是
例如:
那么我们可以使用
The key points of
printf
is the ability to either return a String or a function. Copied from http://www.haskell.org/ghc/docs/6.12.2/html/libraries/base-4.2.0.1/src/Text-Printf.html,and the basic structure we can extract out is
For instance:
then we could use