在Eiffel中,允许使用不从堆中分配的expanded class。从开发人员的角度来看,很少需要考虑从Int到Float的转换,因为它是自动的。我的问题是:为什么Haskell没有选择类似的方法来建模Num。具体来说,让我们考虑一下Int实例。以下是我的问题的基本原理:
[1..3] = [1,2,3]
[1..3.5] = [1.0,2.0,3.0,4.0] -- rounds up
第二个列表是我没想到的,因为根据定义,任意两个整数之间存在无限浮点数。当然,一旦我们测试了序列,很明显它正在返回浮点数的四舍五入。这些转换需要的原因之一是允许我们计算一组整数的平均值。
在Eiffel中,数字类型层次结构对程序员更友好,并且根据需要进行转换:例如,创建序列仍然可以是一组导致浮点均值的Int。这具有可读性优势。
有没有理由在Haskell中没有实现扩展类?任何引用都会有很大帮助。
@ony:关于并行策略的观点:使用原语时我们不会遇到同样的问题吗?手册使用原语进行discourage,这对我来说很有意义,我们可以使用原语,我们可能需要使用抽象类型。在尝试数字均值时遇到的问题是缺少的Fractional Int实例以及为什么5/3不会提升到浮点而不必创建浮点数组来实现相同的结果。必须有一个原因,为什么没有定义Int和Integer的Fractional实例?这可以帮助我更好地理解基本原理。
@letrtroundabout:问题不是关于扩展类本身,而是这个特性可以提供的便利性,尽管单独的这个特性不足以处理从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来使平均功能起作用并且与缺少的Fractional Int相关联,我会喜欢它。虽然这个解释似乎有意义,但我必须知道,如果我清楚我希望有一个双重而我在我的类型签名中说明它是不足以进行适当的转换?
答案 0 :(得分:3)
[a..b]
是enumFromTo a b
类型的Enum
方法的简写。它始于a
和succ
s,直到第一次超过b
。
[a,b..c]
是enumFromThenTo a b c
的简写,类似于enumFromTo
,除了代替succ
每次添加差异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 a
succ
的{{1}}的简写。
[a,b..]
是enumFromThen a b
的简写,只是永远添加(b-a)
。
答案 1 :(得分:1)
至于行为@ J.Abrahamson已经回复了。这是enumFromThenTo
的定义。
至于设计......
实际上GHC有Float#
表示未装箱的类型(可以在任何地方分配,但值很严格)。
由于Haskell是一种惰性语言,因此它假设最初不需要大多数值,直到它们实际引用具有严格参数的原语。
考虑length [2..10]
。在没有优化的情况下,Haskell甚至可以避免生成数字并简单地建立一个列表(没有值)。可能更有用的示例takeWhile (<100) [x*(x-1) | x <- [2..]]
。
但是你不应该认为我们在这里有开销,因为你用的语言用拇指抽出所有东西(除了严格的符号)。 Haskell编译器必须将此作为自己的工作。即当编译器能够告诉列表的所有元素将被引用(转换为普通形式)并且它决定在一个返回的堆栈中处理它时它可以在堆栈上分配它。
使用这种方法,您可以通过使用多个CPU内核从代码中获得更多收益。想象一下,使用Strategies列表在不同的核心上进行处理,因此它们应该在堆上共享公共数据(不在堆栈上)。