2014-10-15 5 views
1

Я хочу сравнить 2 строки rune-by-rune, чтобы увидеть, какой из них идет первым в произвольном алфавитном порядке.Iterate over 2 strings in go

Прямо сейчас у меня есть эта реализация, которая хранит в 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. Есть ли более идиоматический способ перебора более двух строк, чтобы сравнить их руну руны? Я делаю копию данных здесь, чего, вероятно, можно избежать.

EDIT: Чтобы уточнить, что проблема заключается в том, что диапазон (строка) может позволить вам выполнять итерацию по строкам rune by rune, но я не вижу способа перебора более двух строк бок о бок. Только так я вижу его, чтобы преобразовать строки в [] руну.

+1

почему бы не использовать функции сравнения строк в 'sort' пакета? http://golang.org/pkg/sort/#Strings –

+0

@Not_a_Golfer, потому что у него есть собственный алфавит. 'sort.Strings' будет использовать реализацию' sort.StringSlice', сортируя по «нормальному» алфавиту. – thwd

+0

@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'} ' –

ответ

1

Чтобы перебрать два строк бока о боке в методе 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() 
} 
+0

Ницца! это именно то, чего я пытался достичь. Спасибо. Я сравню его с решением otyher. –

+0

Для тех, кто заинтересован, это работает намного лучше. как с точки зрения скорости, так и, конечно, выделения памяти –

2

Вы можете сделать цикл немного более идиоматичным, используя диапазон для одного из двух слов. Это требует добавления проверки внутри цикла, но вам больше не нужно выполнять проверку при окончательном возврате.

// 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 
} 
+0

Да, это выглядит немного лучше, но я думаю, что он работает хуже. –

+0

Я собираюсь выбрать второй ответ, который ближе к тому, что я искал. –

+0

Феликс, вы оценили производительность, или это просто догадка? Возможно, вы правы, но я не могу сказать, просто проверив исходный код. –