来自C++,我发现通用编程是不可或缺的.我想知道人们如何在Haskell中接近它?
说如何在Haskell中编写通用交换函数?
在Haskell中是否存在部分特化的等价概念?
在C++中,我可以部分地使用特殊的泛型交换函数来处理泛型map/hash_map容器,该容器具有O(1)容器交换的特殊交换方法.你如何在Haskell中做到这一点,或者Haskell中泛型编程的典型例子是什么?
这与您关于Haskell和quicksort的其他问题密切相关.我想你可能需要至少阅读介绍一本关于哈斯克尔.听起来好像你还没有掌握关于它的关键点,即它禁止你修改现有变量的值.
交换(正如在C++中所理解和使用的)就其本质而言,都是关于修改现有值的.我们可以使用名称来引用容器,并用完全不同的内容替换该容器,并将该操作专门用于特定容器的快速(和无异常),从而允许我们实现修改和发布方法(对于编写异常安全代码或尝试编写无锁代码至关重要).
你可以在Haskell中编写一个通用交换,但它可能需要一对值并返回一个包含相同值的新对,它们的位置相反,或类似的东西.不是一回事,也没有相同的用途.通过挖掘内部地图并交换其各个成员变量来尝试将其专门化为地图是没有任何意义的,因为你不能在Haskell中做这样的事情(你可以做专业化,但不能修改变量).
假设我们想要"测量"Haskell中的列表:
measure :: [a] -> Integer
这是一种类型声明.这意味着该函数measure
获取任何内容的列表(a
是一个泛型类型参数,因为它以小写字母开头)并返回一个Integer.因此,这适用于任何元素类型的列表 - 它在C++中称为函数模板,或Haskell中的多态函数(与C++中的多态类不同).
我们现在可以通过为每个有趣的案例提供专业化来定义它:
measure [] = 0
即测量空列表,你得到零.
这是一个涵盖所有其他案例的非常一般的定义:
measure (h:r) = 1 + measure r
LHS括号中的位是一种模式.这意味着:拿一个清单,中断并调用它,然后调用剩下的部分r.这些名称是我们可以使用的参数.这将匹配任何列表上至少有一个项目.
如果你在C++中尝试过模板元编程,这对你来说都是旧的,因为它涉及完全相同的样式 - 递归循环,专门化使递归终止.除了在Haskell中它在运行时工作(特定值或模式的函数的特化).
正如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")
在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中的"Generic"通常意味着
polytypic :http:
//haskell.readscheme.org/generic.html但是,aboe我使用泛型的c ++含义.
在Haskell中,您将创建类型类.类型类与OO语言中的类不同.取Numeric类型它说任何作为类实例的东西都可以执行某些操作(+ - */),因此Integer是Numeric的成员,并提供了被认为是Numeric所必需的函数的实现,可以在任何地方使用数字是预期的.
假设您希望能够吸引Ints和Strings.然后,您将Int和String声明为类型类Foo的实例.现在,只要您看到类型(Foo a),就可以使用Int或String.
你不能直接添加整数和浮点数的原因是因为add有类型(数字a)a - > a - > aa是一个类型变量,就像常规变量一样,它只能绑定一次,所以只要你绑定它到Int中列表中的每个a必须是Int.