Funções genéricas do operador personalizado: um caso curioso de uma instrução incorreta *
Eu me deparei com esse problema enquanto brincava com operadores genéricos e personalizados no Swift. No trecho de código abaixo, estou introduzindo dois novos operadores de prefixo, ∑ e ∏, e implemento suas funções de prefixo como soma vetorial e produto, respectivamente. Para não precisar implementar essas e outras funções semelhantes para todos os tipos de números inteiros e de ponto flutuante separadamente, defini dois protocolos: Summable (que requer + implementação) e Multiplicável (que requer * implementação). Além disso, implementei as duas funções dos argumentos SequenceType, que funcionam, por exemplo, com os tipos Array e Rage. Finalmente, você pode ver pelas chamadas println no final do snippet que tudo isso funciona muito bem, exceto por ∏ (1 ... 100). Aqui, o programa trava com EXC_BAD_INSTRUCTION e não há muito mais o que continuar. Observe que ∑ (1 ... 100) funciona, mesmo que seja implementado da mesma maneira. De fato, se eu alterar o valor inicial na linhareturn reduce(s, 1, {$0 * $1})
a 0 do que o programa é concluído sem erros, embora com saídas incorretas das chamadas para ∏.
Então, tudo se resume a usar 0 ou 1 como valor inicial !? Quando o código na linha incorreta é descompactado em várias linhas, fica claro que a falha ocorre em$0 * $1
. Observe também que, em vez dos fechamentos{$0 * $1}
e{$0 + $1}
Eu deveria poder passar as funções de operador + e * diretamente. Infelizmente, isso ofende o compilador: "A aplicação parcial do método genérico não é permitida".
Alguma ideia? Como a troca de 1 (ou qualquer Int diferente de zero) por 0 pode causar uma falha? E por que isso acontece apenas com intervalos para multiplicação, enquanto intervalos para adição com 0 ou 1 valores iniciais funcionam bem?
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
Edição: Embora um pouco embaraçoso para mim, o erro que estou cometendo neste código faz um teste bonito dos seus olhos de programação. Veja se consegue descobrir antes de ler a resposta de Martin abaixo. Você se sentirá bem consigo mesmo quando o fizer. (No entanto, talvez seja necessário procurar outra carreira.)