通用自定义操作员函数:坏的奇怪的情况*指令 [英] Generic Custom Operator Functions: A Curious Case of a Bad * Instruction

查看:258
本文介绍了通用自定义操作员函数:坏的奇怪的情况*指令的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我遇到这个问题,而在Swift中使用泛型和自定义运算符。在下面的代码片段中,我引入两个新的前缀运算符Σ和Π,然后分别实现它们的前缀函数作为向量和和乘积。为了不必为所有整数和浮点类型分别实现这些和类似的功能,我定义了两个协议:Summing(需要+实现)和Multiplicable(需要*实现)。此外,我实现了SequenceType参数的两个函数,例如,使用Array和Rage类型。最后,你可以从代码片段末尾的println调用中看到,除了Π(1 ... 100),这一切都很好地工作。这里程序崩溃与EXC_BAD_INSTRUCTION和没有太多的事情继续。注意,Σ(1 ... 100)工作,即使它以相同的方式实现。事实上,如果我将 return reduce(s,1,{$ 0 * $ 1})中的初始值更改为0,而不是错误,输出到调用Π。

I run into this problem, while playing with generics and custom operators in Swift. In the code snippet below, I am introducing two new prefix operators, ∑ and ∏ and then implement their prefix functions as vector sum and product respectively. In order not to have to implement these and similar functions for all the integer and floating point types separately, I defined two protocols instead: Summable (that requires + implementation) and Multiplicable (that requires * implementation). Also, I implemented the two functions for SequenceType arguments which works, for example, with Array and Rage types. Finally, you can see from the println calls at the end of the snippet that this all works quite nicely, except for ∏(1...100). Here the program crashes with EXC_BAD_INSTRUCTION and not much else to go on. Note that ∑(1...100) works, even though it is implemented in the same way. In fact, if I change the initial value in the line return reduce(s, 1, {$0 * $1}) to 0 than the program completes without error, albeit with wrong outputs from calls to ∏.

所以,它总是归结为使用0或1作为初始值!当冒犯行中的代码通过几行解压缩时,很明显崩溃发生在 $ 0 * $ 1 。还要注意,代替闭包 {$ 0 * $ 1} {$ 0 + $ 1} +和*运算符函数。唉,这冒犯了编译器:不允许通用方法的部分应用。

So, it all boils down to using 0 or 1 as an initial value!? When the code in the offending line is unpacked over several lines, it becomes clear that the crash occurs at $0 * $1. Note also that instead of the closures {$0 * $1} and {$0 + $1} I should be able to pass + and * operator functions directly. Alas, this offends the compiler: "Partial application of generic method is not allowed".

任何想法?如何交换1(或任何非零Int)为0导致崩溃?为什么这只会发生在乘法的范围内,而与0或1个初始值相加的范围工作正常?

Any ideas? How could swapping 1 (or any non zero Int) for 0 cause a crash? And why does this only happen with ranges for multiplication, while ranges for addition with either 0 or 1 initial values works fine?

prefix operator ∑ {}
prefix operator ∏ {}

protocol Summable { func +(lhs: Self, rhs: Self) -> Self }
protocol Multiplicable { func *(lhs: Self, rhs: Self) -> Self }

extension Int: Summable, Multiplicable {}
extension Double: Summable, Multiplicable {}

prefix func ∑<T, S: SequenceType where T == S.Generator.Element, 
    T: protocol<IntegerLiteralConvertible, Summable>>(var s: S) -> T {
    return reduce(s, 0, {$0 + $1})
}

prefix func ∏<T, S: SequenceType where T == S.Generator.Element, 
    T: protocol<IntegerLiteralConvertible, Multiplicable>>(var s: S) -> T {
    return reduce(s, 1, {$0 * $1})
}

let ints = [1, 2, 3, 4]
let doubles: [Double] = [1, 2, 3, 4]

println("∑ints = \( ∑ints )") // --> ∑ints = 10
println("∑doubles = \( ∑doubles )") // --> ∑doubles = 10.0
println("∑(1...100) = \( ∑(1...100) )") // --> ∑(1...100) = 5050

println("∏ints = \( ∏ints )") // --> ∏ints = 24
println("∏doubles = \( ∏doubles )") // --> ∏doubles = 24.0
println("∏(1...100) = \( ∏(1...100) )") // --> CRASH: EXC_BAD_INSTRUCTION

编辑:
虽然我很尴尬,这个代码使你的编程眼睛可爱的测试。看看你能不能在读下面的Martin答案之前。当你这样做时,你会感觉良好。 (我可能需要寻找另一个职业。)

While rather embarrassing for me, the error I am making in this code makes for a cute test of your programming eye. See if you can figure it out before reading Martin's answer below. You'll feel good about yourself when you do. (I however, might need to look for another career.)

推荐答案

这是一个简单的整数溢出。你尝试计算因子

That is a simple integer overflow. You try to compute the factorial

1 * 2 * ... * 100 = 100!
= 93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000
≈ 9.33 × 10^157



根据的 Wolfram Alpha 。使用初始值 0 而不是 1 ,所有产品都为零,溢出不会

according to Wolfram Alpha. With an initial value of 0 instead of 1, all products are zero and the overflow does not occur.

∏(1...20) = 2432902008176640000

按预期工作,是可以存储在
64位整数中的最大阶乘。

works as expected and is the largest factorial that can be stored in a 64-bit integer.

在Swift中,整数计算不会绕过,但如果
结果不适合目标数据类型,则会导致异常。

In Swift, integer calculations do not "wrap around", but cause an exception if the result does not fit into the target datatype.

特殊的溢出操作符
& + & * 为整数计算的行为,
看到的溢出操作符
在Swift文档中。

Swift has special "overflow operators" &+, &*, ... with a different overflow behavior for integer calculations, see "Overflow Operators" in the Swift documentation.

这篇关于通用自定义操作员函数:坏的奇怪的情况*指令的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆