【问题标题】:Iterate over 2 strings in go在go中迭代2个字符串
【发布时间】:2014-10-15 15:29:16
【问题描述】:

我想逐个符文比较 2 个字符串,看看哪个字符串按任意字母顺序排在第一位。

现在我有这个实现,它在map[rune]int 中存储了一个表示我的字母表中字母顺序的映射。

我有这个工作代码。我很清楚当前设计中的缺陷,但这不是问题的重点。

package main

import (
    "bufio"
    "log"
    "math/rand"
    "os"
    "sort"
)

type Dictionnary struct {
    content           []string
    alphaBeticalOrder map[rune]int
}

func minSize(w1, w2 []rune) int {
    if len(w1) < len(w2) {
        return len(w1)
    }
    return len(w2)
}

func (d *Dictionnary) comesFirst(a, b rune) int {

    return d.alphaBeticalOrder[a] - d.alphaBeticalOrder[b]
}

func (d Dictionnary) Less(i, j int) bool {
    wordi, wordj := []rune(d.content[i]), []rune(d.content[j])
    size := minSize(wordi, wordj)
    for index := 0; index < size; index++ {
        diff := d.comesFirst(wordi[index], wordj[index])
        switch {
        case diff < 0:
            return true
        case diff > 0:
            return false
        default:
            continue
        }
    }
    return len(wordi) < len(wordj)
}

func (d Dictionnary) Swap(i, j int) {
    d.content[i], d.content[j] = d.content[j], d.content[i]
}

func (d Dictionnary) Len() int {
    return len(d.content)
}

func main() {

    letters := []rune{'z', 'y', 'x', 'w', 'v', 'u', 't', 's', 'r', 'q', 'p', 'o', 'n', 'm', 'l', 'k', 'j', 'i', 'h', 'g', 'f', 'e', 'd', 'c', 'b', 'a'}
    aOrder := make(map[rune]int)
    perm := rand.Perm(len(letters))
    for i, v := range perm {
        aOrder[letters[i]] = v
    }

    file, err := os.Open("testdata/corpus.txt")
    if err != nil {
        log.Fatal(err)
    }

    corpus := make([]string, 0, 1000)
    scanner := bufio.NewScanner(file)
    for scanner.Scan() {
        corpus = append(corpus, scanner.Text())
    }

    if err := scanner.Err(); err != nil {
        log.Fatal(err)
    }
    file.Close()

    input := Dictionnary{content: corpus, alphaBeticalOrder: aOrder}

    sort.Sort(input)

    ofile, err := os.Create("testdata/sorted.txt")
    writer := bufio.NewWriter(ofile)
    for _, v := range input.content {
        writer.WriteString(v)
        writer.WriteString("\n")
    }
    writer.Flush()
    defer ofile.Close()
}

我的问题与Less(i,j int) bool 函数有关。是否有更惯用的方法来迭代 2 个字符串以逐个比较它们 rune ?我正在这里复制数据,这可能是可以避免的。

编辑: 为了澄清我的问题,range(string) 可以让您逐个遍历字符串 rune,但我看不到并排迭代 2 个字符串的方法。我看到它的唯一方法是将字符串转换为 []rune。

【问题讨论】:

  • 为什么不使用sort包中的字符串比较函数呢? golang.org/pkg/sort/#Strings
  • @Not_a_Golfer 因为他有一个自定义字母表。 sort.Strings 将使用 sort.StringSlice 的实现,按“正常”字母排序。
  • @tomwilde 在我看来并不那么习惯[]rune{'z', 'y', 'x', 'w', 'v', 'u', 't', 's', 'r', 'q', 'p', 'o', 'n', 'm', 'l', 'k', 'j', 'i', 'h', 'g', 'f', 'e', 'd', 'c', 'b', 'a'}
  • 也许 OP 应该澄清一下;我将“任意字母顺序”视为任何可能的顺序。
  • @Not_a_Golfer 最初我的目标是创建一个足够大的数据集来测试逆算法(从排序的单词列表中猜测字母顺序),我有点不知所措。

标签: string go rune


【解决方案1】:

您可以使用两个单词之一的范围使循环更加惯用。 这需要在循环中添加检查,但您不再需要在最终返回时执行检查。

// determines if the word indexed at i is less than the word indexed at j.
func (d Dictionnary) Less(i, j int) bool {
    wordi, wordj := []rune(d.content[i]), []rune(d.content[j])
    for i, c := range wordi {
        if i == len(wordj) {
            return false
        }

        diff := d.comesFirst(c, wordj[i])
        switch {
        case diff < 0:
            return true
        case diff > 0:
            return false
        default:
            continue
        }
    }
    return false
}

【讨论】:

  • 是的,看起来好一点,但我认为它的表现更差。
  • 我将选择更接近我所寻找的第二个答案。
  • Felix,您是否衡量了性能,或者这只是一种预感?您可能是对的,但我不能仅通过检查源代码来判断。
【解决方案2】:

Less 方法中并排迭代两个字符串:

package main

import (
    "bufio"
    "log"
    "math/rand"
    "os"
    "sort"
    "unicode/utf8"
)

type Dictionary struct {
    content           []string
    alphaBeticalOrder map[rune]int
}

func (d Dictionary) Len() int {
    return len(d.content)
}

func (d Dictionary) Swap(i, j int) {
    d.content[i], d.content[j] = d.content[j], d.content[i]
}

func (d Dictionary) Less(i, j int) bool {
    wi, wj := d.content[i], d.content[j]
    jj := 0
    for _, ri := range wi {
        rj, size := utf8.DecodeRuneInString(wj[jj:])
        if rj == utf8.RuneError && size == 0 {
            return false
        }
        switch ao := d.alphaBeticalOrder[ri] - d.alphaBeticalOrder[rj]; {
        case ao < 0:
            return true
        case ao > 0:
            return false
        }
        jj += size
    }
    return len(wi) < len(wj)
}

func main() {

    letters := []rune{'z', 'y', 'x', 'w', 'v', 'u', 't', 's', 'r', 'q', 'p', 'o', 'n', 'm', 'l', 'k', 'j', 'i', 'h', 'g', 'f', 'e', 'd', 'c', 'b', 'a'}
    aOrder := make(map[rune]int)
    perm := rand.Perm(len(letters))
    for i, v := range perm {
        aOrder[letters[i]] = v
    }

    file, err := os.Open("testdata/corpus.txt")
    if err != nil {
        log.Fatal(err)
    }

    corpus := make([]string, 0, 1000)
    scanner := bufio.NewScanner(file)
    for scanner.Scan() {
        corpus = append(corpus, scanner.Text())
    }

    if err := scanner.Err(); err != nil {
        log.Fatal(err)
    }
    file.Close()

    input := Dictionary{content: corpus, alphaBeticalOrder: aOrder}

    sort.Sort(input)

    ofile, err := os.Create("testdata/sorted.txt")
    writer := bufio.NewWriter(ofile)
    for _, v := range input.content {
        writer.WriteString(v)
        writer.WriteString("\n")
    }
    writer.Flush()
    defer ofile.Close()
}

【讨论】:

  • 不错!这正是我想要达到的目标。谢谢你。我会将它与其他实现进行基准测试。
  • 对于那些感兴趣的人来说,这表现得更好。无论是在速度方面,当然还是在内存分配方面
猜你喜欢
  • 2010-11-12
  • 2013-12-11
  • 2019-07-15
  • 2014-04-21
  • 2010-12-01
  • 2012-03-06
  • 1970-01-01
  • 1970-01-01
  • 2012-10-23
相关资源
最近更新 更多