2017-01-19 1 views
2

次のコードスニペットを見てみましょう:無限ループにおけるジェネリック医薬品の結果を使用してOptionalsためのオーバーライド/オペレータ

func/<T>(lhs: T?,rhs: T?) throws -> T? { 
    switch (lhs,rhs) { 
     case let (l?,r?): 
      return try l/r 
     default: 
      return nil 
    } 
} 

let x : Double? = 2 
let y : Double? = 2 

let z = try! x/y 

私は2つのオプションのパラメータを期待する汎用関数を作成しました。このコードを実行すると無限ループにつながります。を試してみてくださいfunc/<T>(lhs: T?,rhs: T?)を使用して値を分割してください。誰も2つのnoneオプションのdouble値を分割すると、私が書いたメソッドに関数呼び出しを行い、デフォルトではないと説明することができます/演算子の定義double

私はすべてが魔法のように動作し、そのクラスの/静的オペレータを必要と拡張子によってダブルを拡張する場合:

protocol Dividable { 
    static func /(lhs: Self, rhs: Self) -> Self 
} 

extension Double: Dividable {} 

func/<T:Dividable>(lhs: T?,rhs: T?) throws -> T? { 
    switch (lhs,rhs) { 
     case let (l?,r?): 
      return l/r 
     default: 
      return nil 
    } 
} 

let x : Double? = 2 
let y : Double? = 2 

let z = try! x/y 
+3

をごswitch文を包みます'/'のビルトイン演算子は投げません。さらに、あなたの演算子が投げていないように見えるので、 'throws'とマークすることは実際に投げてしまわないので、実際には違いはありません。 – xoudini

+0

@xoudini私は上記のupvotedのコメントで推測された理由が真ではないことを指摘しているかもしれません: 'try'を使って非投げの関数をマークすることができます(警告します)コールのオーバーロード解決で'l/r'がOPが使用しようとしているオーバーロードを見つけられない理由は、以下のアンウォールでカバーされています。しかし、上記のコメントの2番目の部分は関連しています(なぜスロー?)。 – dfri

+0

私はそれについて考えました。2つのダブルスを分割すると例外(0で割る)が発生する可能性があるので、** l/r **にtryをつける必要があります。その関数の呼び出し元は、失敗する可能性があることを知っています。 –

答えて

1

あなたの関数のシグネチャは、コンパイラがについて何も知らせていませんlhsrhsのタイプは、同じタイプではありません。 (この場合はStringで)同じ型の2つのパラメータにかかる/と呼ばれるコンパイラが知っている唯一の方法があるので、これは無限ループになります

let str1 = "Left string" 
let str2 = "Right string" 
let result = try? str1/str2 

:たとえば、あなたはこのようにあなたのメソッドを呼び出すことができますあなたが宣言したものreturn try l/rは、あなたのfunc/<T>(lhs: T?,rhs: T?) throws -> T?メソッドを何度も繰り返し呼び出すでしょう。

あなたの質問で言及したように、パラメータが準拠しなければならないプロトコルが必要です。残念ながらthere is no existing Number or Dividable protocol that would fit your needsなので、自分で作る必要があります。分母が0であり、あなたはそれがあるように、あなたの関数からthrowsキーワードを削除することができるはずですので、エラーをスローしないとき部門がクラッシュすること

注:

func/<T:Dividable>(lhs: T?, rhs: T?) -> T?

編集さらに明確にする

コンパイラがその時点で何を知っているか考えてみると、もっと意味があると思います。コンパイラが知っている関数の中に入ると、lhsrhsTとオプションです。 何がTであるか、またはそのプロパティまたは機能のいずれかがわからないが、どちらもタイプがTであることのみを知っている。一度値をアンラップすると、はまだで、どちらもタイプがTで、オプションではないことがわかります。 でもT(この例では)はDoubleであることがわかりますが、これはString(上記の例のとおり)です。これは、コンパイラがすべての可能なクラスと構造体を反復して、あなたのメソッドシグニチャ(この場合はfunc/(lhs: Double, rhs: Double) -> Double)をサポートするものを見つけることを必要とします。これは単に妥当な時間内にはできず、予測できないコードにつながります。このグローバルメソッドを追加した後に、/が既存のもの(例えばFloat(10)/Float(5)など)に使用されるたびに、メソッドが呼び出されるたびに、かなり面倒で混乱することがありました。

+0

さて、スロー部分は私にとって意味がありますが、Tをある種のプロトコルに制約することなく、関数シグネチャから「スロー」を取り除くだけで、何らかの理由で無限ループにつながります。私が "return = l/r"と呼ぶことは意味がありません。ここでlとrは普通のdouble型(オプションなし)で、私が書いた関数への呼び出しになります。** public func /(lhs:Double 、rhs:Double) - > Double ** Swiftの実装から、私の関数は2つのオプションTを期待しているので?ただTだけではありません。 –

+1

私は説明を追加しました。うまくいけばそれは少し助ける:) –

2

たとえば、DoubleコンクリートDoubleタイプを使用して実装されていませんが、FloatingPointに準拠したタイプのためではなく、デフォルトとして、一般的な実装:カスタム/機能のブロック内の

は、コンパイラがあることを知りません。タイプホルダーTFloatingPointに準拠しており、l/rのオーバーロード解像度はメソッド自体に解決されます(FloatingPointの実装はより具体的ですがより一般的な非制約型カスタム実装ではTと入力してください)。

独自のカスタム方法にも型制約としてFloatingPointを追加することでこの問題を回避できます。

func /<T: FloatingPoint>(lhs: T?, rhs: T?) throws -> T? { 
    switch (lhs, rhs) { 
     case let (l?, r?): 
      return try l/r 
     default: 
      return nil 
    } 
} 

同様に、整数型のバイナリ算術を内部に準拠したタイプに制限デフォルトの一般的な実装として実装されていますプロトコル_IntegerArithmetic、これには公開プロトコルIntegerArithmeticが準拠しています。

あなたは整数型に対してカスタム演算子関数のオーバーロードを実装するために、後者のパブリックプロトコルを使用することができます。

func /<T: IntegerArithmetic>(lhs: T?, rhs: T?) throws -> T? { 
    switch (lhs, rhs) { 
     case let (l?, r?): 
      return try l/r 
     default: 
      return nil 
    } 
} 

最後に、この機能をスローする理由を考えてみるとよいでしょう。両方の場合にのみ操作したい2つのオプションの値を扱うときは、実装を簡素化する方法があります。nil。例えば:あなたは簡潔上のセマンティクスを好む場合の

func /<T: FloatingPoint>(lhs: T?, rhs: T?) -> T? { 
    return lhs.flatMap { l in rhs.map{ l/$0 } } 
} 

func /<T: IntegerArithmetic>(lhs: T?, rhs: T?) -> T? { 
    return lhs.flatMap { l in rhs.map{ l/$0 } } 
} 

、それはおそらく以来、原因あなたのreturn文で `try`に再帰につながる単一if文で

func /<T: FloatingPoint>(lhs: T?, rhs: T?) -> T? { 
    if case let (l?, r?) = (lhs, rhs) { 
     return l/r 
    } 
    return nil 
} 

func /<T: IntegerArithmetic>(lhs: T?, rhs: T?) -> T? { 
    if case let (l?, r?) = (lhs, rhs) { 
     return l/r 
    } 
    return nil 
} 
関連する問題