通用自定义运算符函数:一个糟糕的 * 指令的奇怪案例 [英] Generic Custom Operator Functions: A Curious Case of a Bad * Instruction

查看:14
本文介绍了通用自定义运算符函数:一个糟糕的 * 指令的奇怪案例的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在 Swift 中使用泛型和自定义运算符时遇到了这个问题.在下面的代码片段中,我引入了两个新的前缀运算符,∑和∏,然后将它们的前缀函数分别实现为向量和和乘积.为了不必分别为所有整数和浮点类型实现这些和类似的功能,我定义了两个协议:Summable(需要 + 实现)和 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

虽然对我来说相当尴尬,但我在这段代码中所犯的错误对你的编程眼光进行了一次可爱的测试.在阅读下面马丁的答案之前,看看你是否能弄清楚.当你这样做时,你会对自己感觉良好.(不过,我可能需要寻找其他职业.)

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 文档中.

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

这篇关于通用自定义运算符函数:一个糟糕的 * 指令的奇怪案例的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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