web-dev-qa-db-ja.com

同等のインターフェースとは何ですか?

私は学習目的でGoで単純なリンクリストの実装に取り​​組んでいます。要素の定義は次のとおりです。

_type Element struct {
    next, prev *Element
    Value      interface{}
}
_

ご覧のとおり、値は空のインターフェイスを満たすものであれば何でもかまいません。さて、新機能として、新しい要素をリストに挿入すると、並べ替えられた方法で挿入されるようにしたいと思います。各要素は<=次の要素になります。

これを行うために、私は次のメソッドを作成しました。

_func (l *LinkedList) Add(val interface{}) *Element {
    this := &l.Root
    e := Element{Value: val}
    for {
        if this.next.Value != nil && this.next.Value < val {  // <-comparison here
            this = this.next
        } else {
            return l.insert(&e, this)
        }
    }
}
_

コンパイラは_operator < not defined on interface_に文句を言いますが、これは公平です。したがって、Element typedefで、Valueを_<_演算子を使用して比較できるタイプに制限する必要があることを理解しています。 Goが演算子のオーバーロードをサポートしていないというこの問題を調査しているときに、これを学びました。私はそうしようとはしていません。代わりに、Element.Valueが_<_演算子を使用して比較できる型であることを確認しようとしています。どうすればよいですか?

更新:

ビルトインに基づいて、いくつかの関数を介して比較できる新しい型を簡単に定義することはそれほど難しくないかもしれないと私は思います。だから私はこの混乱を書いた(そして同じことをしようとする他のたくさんの方法も):

_type Comparable interface {
    LessThan(j interface{}) bool // tried (j Comparable), (j MyInt), etc
    EqualTo(j interface{}) bool  // tried (j Comparable), (j MyInt), etc
}

type MyInt int

func (i MyInt) LessThan(j MyInt) bool {
    return i < j
}

func (i MyInt) EqualTo(j MyInt) bool {
    return i == j
}

type Element struct {
    next, prev *Element
    Value      Comparable
}
_

私が本当に望んでいるのは、型に実装された場合、その型の2つのインスタンスで動作する関数LessThanEqualToを提供し、ブール値を提供するインターフェイスを定義することです-LessThan(i, j WhatEvers) boolのようなもの_<_の代わりに使用できます。以下では、インスタンスメソッドとして実装されていることに気付きました。両方の方法を試しましたが、成功しませんでした。上記では、Add関数でthis.next.Value.LessThan(val)のように使用します。私はどちらかを取得します:

_linkedlist.MyInt does not implement linkedlist.Comparable (wrong type for EqualTo method)
    have EqualTo(linkedlist.MyInt) bool
    want EqualTo(interface {}) bool
_

または

_linkedlist.MyInt does not implement linkedlist.Comparable (wrong type for EqualTo method)
    have EqualTo(linkedlist.MyInt) bool
    want EqualTo(linkedlist.Comparable) bool
_

これは、インターフェイスを使用して、カスタムタイプの2つのインスタンスで動作する特定の関数が存在する必要があることを要求することは可能ですか、それともメソッド専用ですか?

10
domoarigato

編集:
このユーザータイプを検討してください:

type userType struct {
    frequency int
    value     rune
}

そして、このタイプをリンクリストに追加したいとします。
そして、最初に頻度でソートする必要があります。次に、頻度が同じである場合は、char値を確認します。したがって、Compare関数は次のようになります。

func (a userType) Compare(b userType) int {
    if a.frequency > b.frequency {
        return 1
    }
    if a.frequency < b.frequency {
        return -1
    }
    if a.value > b.value {
        return 1
    }
    if a.value < b.value {
        return -1
    }
    return 0
}

これはこのインターフェースを満たします:

type Comparer interface {
    Compare(b userType) int
}

これらを追加します{1,'d'} {2,'b'} {3,'c'} {4,'a'} {4,'b'} {4,'c'} LinkeListへのタイプ:
サンプルコード:

package main

import (
    "container/list"
    "fmt"
)

type Comparer interface {
    Compare(b userType) int
}

type userType struct {
    frequency int
    value     rune
}

// it should sort by frequency first, then if the frequencies are the same, look at the char value.
func (a userType) Compare(b userType) int {
    if a.frequency > b.frequency {
        return 1
    }
    if a.frequency < b.frequency {
        return -1
    }
    if a.value > b.value {
        return 1
    }
    if a.value < b.value {
        return -1
    }
    return 0
}

func Insert(val userType, l *list.List) {
    e := l.Front()
    if e == nil {
        l.PushFront(val)
        return
    }
    for ; e != nil; e = e.Next() {
        var ut userType = e.Value.(userType)
        if val.Compare(ut) < 0 {
            l.InsertBefore(val, e)
            return
        }
    }
    l.PushBack(val)
}

func main() {
    l := list.New()
    Insert(userType{4, 'c'}, l)
    Insert(userType{4, 'a'}, l)
    Insert(userType{4, 'b'}, l)
    Insert(userType{2, 'b'}, l)
    Insert(userType{3, 'c'}, l)
    Insert(userType{1, 'd'}, l)
    for e := l.Front(); e != nil; e = e.Next() {
        ut := e.Value.(userType)
        fmt.Printf("{%d,%q} ", ut.frequency, ut.value)
    }
    fmt.Println()

    var t interface{} = userType{4, 'c'}
    i, ok := t.(Comparer)
    fmt.Println(i, ok)
}

および出力:

{1,'d'} {2,'b'} {3,'c'} {4,'a'} {4,'b'} {4,'c'} 
{4 99} true

したがって、既知のタイプ(intなど)を使用する準備ができている場合は、次のサンプルを参照してください。

package main

import (
    "container/list"
    "fmt"
)

func Insert(val int, l *list.List) {
    e := l.Front()
    if e == nil {
        l.PushFront(val)
        return
    }
    for ; e != nil; e = e.Next() {
        v := e.Value.(int)
        if val < v {
            l.InsertBefore(val, e)
            return
        }
    }
    l.PushBack(val)
}

func main() {
    l := list.New()
    Insert(4, l)
    Insert(2, l)
    Insert(3, l)
    Insert(1, l)
    for e := l.Front(); e != nil; e = e.Next() {
        fmt.Print(e.Value, " ") // 1 2 3 4
    }
    fmt.Println()
}

古い:

Goにはそのようなインターフェースはありません。次のLess関数を記述して、タイプを比較できます。

func Less(a, b interface{}) bool {
    switch a.(type) {
    case int:
        if ai, ok := a.(int); ok {
            if bi, ok := b.(int); ok {
                return ai < bi
            }
        }
    case string:
        if ai, ok := a.(string); ok {
            if bi, ok := b.(string); ok {
                return ai < bi
            }
        }
    // ...
    default:
        panic("Unknown")
    }
    return false
}

テストサンプルコード:

package main

import (
    "container/list"
    "fmt"
)

func Less(a, b interface{}) bool {
    switch a.(type) {
    case int:
        if ai, ok := a.(int); ok {
            if bi, ok := b.(int); ok {
                return ai < bi
            }
        }
    case string:
        if ai, ok := a.(string); ok {
            if bi, ok := b.(string); ok {
                return ai < bi
            }
        }
    default:
        panic("Unknown")
    }
    return false
}

func Insert(val interface{}, l *list.List) *list.Element {
    e := l.Front()
    if e == nil {
        return l.PushFront(val)
    }
    for ; e != nil; e = e.Next() {
        if Less(val, e.Value) {
            return l.InsertBefore(val, e)
        }
    }
    return l.PushBack(val)
}

func main() {
    l := list.New()

    Insert(4, l)
    Insert(2, l)
    Insert(3, l)
    Insert(1, l)
    for e := l.Front(); e != nil; e = e.Next() {
        fmt.Print(e.Value, " ")
    }
    fmt.Println()

    Insert("C", l)
    Insert("A", l)
    Insert("AB", l)
    Insert("C", l)
    Insert("C2", l)
    Insert("C1", l)

    for e := l.Front(); e != nil; e = e.Next() {
        fmt.Print(e.Value, " ")
    }
    fmt.Println()
}

出力:

1 2 3 4 
1 2 3 4 A AB C C C1 C2 
3
user6169399

これは私が仕事をすることができた1つの可能な答えですが、それはとても醜いです(int値とのすべての比較を減らします-文字列ではうまくいくかもしれませんが、他のタイプでは血まみれになります)-私はそれを受け入れたくありません:

type MyInt int

type Valuer interface {
    ValueOf() int
}

func LessThan(i, j Valuer) bool {
    return i.ValueOf() < j.ValueOf()
}

func EqualTo(i, j Valuer) bool {
    return i.ValueOf() == j.ValueOf()
}

func (i MyInt) ValueOf() int {
    return int(i)
}

type Element struct {
    next, prev *Element
    Value      Valuer
}

誰かがもっと良い方法があると教えてください!

0
domoarigato