11

来自 C++,我发现泛型编程必不可少。我想知道人们如何在 Haskell 中处理这个问题?

说一下如何在 Haskell 中编写通用交换函数?

Haskell中是否有等效的部分专业化概念?

在 C++ 中,我可以将通用交换函数部分专门化为通用 map/hash_map 容器的特殊函数,该容器具有 O(1) 容器交换的特殊交换方法。你是如何在 Haskell 中做到这一点的,或者 Haskell 中泛型编程的典型例子是什么?

4

5 回答 5

31

这与您关于 Haskell 和快速排序的其他问题密切相关。我认为您可能至少需要阅读一本关于 Haskell 的书的介绍。听起来您似乎还没有掌握它的关键点,那就是它禁止您修改现有变量的值。

交换(在 C++ 中被理解和使用)就其本质而言,都是关于修改现有值的。这样我们就可以使用一个名称来引用一个容器,并用完全不同的内容替换该容器,并将该操作专门化为特定容器的快速(且无异常),从而允许我们实现修改和发布的方法(对于编写异常安全代码或尝试编写无锁代码至关重要)。

您可以在 Haskell 中编写通用交换,但它可能需要一对值并返回一个包含相同值但位置相反的新对,或类似的东西。不是真的一样的东西,也没有相同的用途。通过在地图内部挖掘并交换其各个成员变量来尝试将其专门用于地图是没有任何意义的,因为您只是不允许在 Haskell 中做类似的事情(您可以进行专门化,但不能变量的修改)。

假设我们想在 Haskell 中“测量”一个列表:

measure :: [a] -> Integer

那是一个类型声明。这意味着该函数measure接受任何内容的列表(a是泛型类型参数,因为它以小写字母开头)并返回一个整数。所以这适用于任何元素类型的列表——它在 C++ 中称为函数模板,或在 Haskell 中称为多态函数(与 C++ 中的多态类不同)。

我们现在可以通过为每个有趣的案例提供专业化来定义它:

measure [] = 0

即测量空列表,你得到零。

这是一个涵盖所有其他情况的非常笼统的定义:

measure (h:r) = 1 + measure r

LHS 括号中的位是一个模式。它的意思是:取一个列表,把头部折断,叫它h,剩下的部分叫r。这些名称是我们可以使用的参数。这将匹配任何包含至少一项的列表。

如果您在 C++ 中尝试过模板元编程,那么这对您来说都是老生常谈了,因为它涉及完全相同的风格 - 递归执行循环,特化使递归终止。除了在 Haskell 中它在运行时工作(针对特定值或值模式的函数的专门化)。

于 2008-12-18T08:01:27.857 回答
9

正如 Earwicker 所说,这个例子在 Haskell 中意义不大。如果你绝对想要它,这里有类似的东西(交换一对的两个部分),来自交互式会话的 c&p:

GHCi, version 6.8.2: http://www.haskell.org/ghc/  :? for help
Loading package base ... linking ... done.
Prelude> let swap (a,b) = (b,a)
Prelude> swap("hello", "world")
("world","hello")
Prelude> swap(1,2)
(2,1)
Prelude> swap("hello",2)
(2,"hello")
于 2008-12-18T08:28:06.870 回答
6

在 Haskell 中,函数尽可能是通用的(多态的)——编译器会推断出“最通用的类​​型”。例如,TheMarko 的示例交换在没有类型签名的情况下默认是多态的:

*Main> let swap (a,b) = (b,a)
*Main> :t swap
swap :: (t, t1) -> (t1, t)

至于部分特化,ghc 有一个非 98 扩展名:
file:///C:/ghc/ghc-6.10.1/doc/users_guide/pragmas.html#specialize-pragma

另外,请注意术语不匹配。在 c++、Java 和 C# 中所谓的泛型在 Haskell 中称为多态。Haskell 中的“通用”通常表示多类型:http: //haskell.readscheme.org/generic.html
但是,我使用通用的 c++ 含义。

于 2008-12-19T23:21:24.097 回答
5

在 Haskell 中,您将创建类型类。类型类不像 OO 语言中的类。以 Numeric 类型类为例,它表示类的任何实例都可以执行某些操作(+ - * /),因此 Integer 是 Numeric 的成员,并提供了被视为 Numeric 所需的函数的实现,并且可以在任何地方使用预计为数字。

假设您希望能够 foo 整数和字符串。然后将 Int 和 String 声明为 Foo 类型类的实例。现在,无论您在哪里看到类型 (Foo a),您现在都可以使用 Int 或 String。

不能直接添加整数和浮点数的原因是因为 add 具有类型 (Numeric a) a -> a -> aa 是一个类型变量,就像常规变量一样,它只能绑定一次,所以只要你绑定它对 Int 列表中的每个 a 都必须是 Int。

于 2009-09-01T16:35:13.340 回答
2

在阅读了 Haskell 书籍以真正理解 Earwicker 的答案之后,我建议您也阅读有关类型类的内容。我不确定“部分专业化”是什么意思,但听起来他们可以接近。

于 2008-12-19T22:38:33.510 回答