-1

在 Eiffel 中,允许使用不从堆中分配的扩展类。从开发人员的角度来看,很少需要考虑从 Int 到 Float 的转换,因为它是自动的。我的问题是:为什么 Haskell 没有选择类似的方法来建模 Num。具体来说,让我们考虑 Int 实例。这是我提出问题的理由:

       [1..3] = [1,2,3]
       [1..3.5] = [1.0,2.0,3.0,4.0] -- rounds up

第二个列表是我没想到的,因为根据定义,在任何两个整数之间都有无限的浮点数。当然,一旦我们测试了这个序列,很明显它会返回四舍五入的浮点数的底数。需要这些转换的原因之一是允许我们计算一组整数的平均值。

在 Eiffel 中,数字类型层次结构对程序员更友好一些,并且可以根据需要进行转换:例如,创建一个序列仍然可以是一组导致浮点均值的 Ints。这具有可读性优势。

扩展类没有在 Haskell 中实现是有原因的吗?任何参考都会有很大帮助。

@ony:关于并行策略的要点:使用原语时我们不会面临同样的问题吗?该手册确实不鼓励使用原语,这对我来说一般来说是有意义的,只要我们可以使用原语,我们可能需要使用抽象类型。我在尝试对数字求平均值时遇到的问题是缺少 Fractional Int 实例,以及为什么 5/3 不提升为浮点而不是创建浮点数组来获得相同的结果。为什么没有定义 Int 和 Integer 的 Fractional 实例一定是有原因的?这可以帮助我更好地理解其中的原理。

@leftroundabout:问题不在于扩展类本身,而是此类功能可以提供的便利性,尽管仅该功能不足以处理从 int 浮动的类型提升,例如我在对@ony 的回复中提到的。让我们以均值的经典示例为例,尝试将其定义为

> [Int] :: Double
  let mean xs = sum xs / length xs (--not valid haskell code)
  [Int] :: Double
  let mean = sum xs / fromIntegral (length xs)

如果我不必调用 fromIntegral 来使 mean 函数工作并且与缺少的Fractional Int相关联,我会喜欢它。虽然解释似乎有道理,但它必须,我不明白的是,如果我清楚我期望一个 double 并且我在我的类型签名中声明它是否不足以进行适当的转换?

4

2 回答 2

3

[a..b]是 typeclass 的enumFromTo a b一种方法的简写Enum。它从asuccs 开始,直到b超过第一个时间。

[a,b..c]is 的简写enumFromThenTo a b c类似于enumFromToexcept 而不是succing 它b-a每次都添加差异。默认情况下,此差异是通过往返计算的,Int因此可能会或可能不会尊重分数差异。也就是说,Double按您的预期工作

Prelude> [0.0, 0.5.. 10]
[0.0,0.5,1.0,1.5,2.0,2.5,3.0,3.5,4.0,4.5,5.0,5.5,6.0,6.5,7.0,7.5,8.0,8.5,9.0,9.5,10.0]

[a..]enumFrom asucc永远的简写。

[a,b..]是永远enumFromThen a b添加的简写。(b-a)

于 2014-01-17T13:54:11.590 回答
1

至于行为@J.Abrahamson 已经回复了。那是定义enumFromThenTo

至于设计...实际上 GHC 有Float#代表未装箱的类型(可以在任何地方分配,但值是严格的)。由于 Haskell 是一种惰性语言,它假定大多数值最初都不是必需的,直到它们实际使用带有严格参数的原语来引用。考虑length [2..10]。在这种情况下,如果没有优化,Haskell 甚至可以避免生成数字并简单地建立一个列表(没有值)。可能更有用的例子takeWhile (<100) [x*(x-1) | x <- [2..]]

但是你不应该认为我们在这里有开销,因为你使用的语言是用拇指抽象出所有东西的(除了严格的符号)。Haskell 编译器必须将此作为自己的工作。即,当编译器能够告诉列表的所有元素都将被引用(转换为正常形式)并且它决定在一个返回堆栈中处理它时,它可以在堆栈上分配它。

同样通过这种方法,您可以通过使用多个 CPU 内核从代码中获得更多收益。想象一下,使用Strategies您的列表正在不同的内核上处理,因此它们应该在堆上(而不是在堆栈上)共享公共数据。

于 2014-01-17T14:21:35.293 回答