当前位置: 首页 > 知识库问答 >
问题:

如何定义一个自定义的字母顺序比较和排序字符串在围棋?

赵渊
2023-03-14

在将此标记为副本之前,请阅读到底部

我希望能够按照字母顺序对字符串数组(或基于一个字符串值的结构片)进行排序,但要基于自定义字母表或unicode字母。

大多数时候,人们建议使用支持不同预定义语言环境/字母表的拼贴器。(请参阅此Java答案),但对于这些语言环境捆绑包中不可用的稀有语言/字母表,可以做些什么?

我想使用的语言在Golangs的归类支持和可用的语言列表中不可用,所以我需要能够定义自定义字母表,或用于排序的Unicode字符/符文的顺序。

其他人建议先将字符串翻译成英文/ASCII可排序字母表,然后进行排序。这就是在Javascript的解决方案或Ruby的解决方案中提出的类似问题。但是肯定有更有效的方法来实现这一点。

是否可以在Go中创建使用自定义字母/字符集的拼贴器?这就是func NewFromTable的用途吗?

看起来我应该能够使用Reorder函数,但这似乎还没有在语言中实现?源代码显示如下:

func Reorder(s ...string) Option {
    // TODO: need fractional weights to implement this.
    panic("TODO: implement")
}

如何定义一个自html" target="_blank">定义的字母顺序比较和排序字符串在围棋?

共有2个答案

田博易
2023-03-14

使用table\u测试。go[1]作为一个起点,我提出了以下几点。真正的工作是由Builder完成的。添加[2]:

package main

import (
   "golang.org/x/text/collate"
   "golang.org/x/text/collate/build"
)

type entry struct {
   r rune
   w int
}

func newCollator(ents []entry) (*collate.Collator, error) {
   b := build.NewBuilder()
   for _, ent := range ents {
      err := b.Add([]rune{ent.r}, [][]int{{ent.w}}, nil)
      if err != nil { return nil, err }
   }
   t, err := b.Build()
   if err != nil { return nil, err }
   return collate.NewFromTable(t), nil
}

结果:

package main
import "fmt"

func main() {
   a := []entry{
      {'a', 3}, {'b', 2}, {'c', 1},
   }
   c, err := newCollator(a)
   if err != nil {
      panic(err)
   }
   x := []string{"alfa", "bravo", "charlie"}
   c.SortStrings(x)
   fmt.Println(x) // [charlie bravo alfa]
}
  1. https://github.com/golang/text/blob/3115f89c/collate/table_test.go
  2. https://pkg.go.dev/golang.org/x/text/collate/build#Builder.Add
隆兴修
2023-03-14

事先注意:

以下解决方案已被清理和优化,并在此处作为可重用库发布:github。com/icza/abcsort

使用abcort,自定义排序字符串切片(使用自定义字母表)非常简单:

sorter := abcsort.New("bac")

ss := []string{"abc", "bac", "cba", "CCC"}
sorter.Strings(ss)
fmt.Println(ss)

// Output: [CCC bac abc cba]

按一个struct字段对结构片进行自定义排序,如下所示:

type Person struct {
    Name string
    Age  int
}

ps := []Person{{Name: "alice", Age: 21}, {Name: "bob", Age: 12}}
sorter.Slice(ps, func(i int) string { return ps[i].Name })
fmt.Println(ps)

// Output: [{bob 12} {alice 21}]

原答覆如下:

我们可以实现使用自定义字母表的自定义排序。我们只需要创建适当的less(i,j int)bool函数,sort包将完成其余的工作。

问题是如何创建这样一个less()函数?

让我们从定义自定义字母表开始。方便的方法是创建一个字符串,该字符串包含自定义字母表的字母,从最小到最大进行枚举(排序)。例如:

const alphabet = "bca"

让我们从这个字母表创建一个地图,它将告诉我们自定义字母表中每个字母的重量或顺序:

var weights = map[rune]int{}

func init() {
    for i, r := range alphabet {
        weights[r] = i
    }
}

(注意:上面循环中的i是byte索引,而不是rune索引,但是由于两者都是单调增加的,所以对于rune权重来说两者都很好。)

现在我们可以创建我们的less()函数。要具有“可接受”的性能,我们应该避免将输入字符串值转换为字节或rune切片。为此,我们可以从utf8调用aid。DecodeRuneInString()函数,它解码字符串的第一个rune

所以我们逐个比较符文。如果两个符文都是自定义字母表的字母,我们可以使用它们的权重来判断它们之间的比较。如果至少有一个符文不是来自我们的自定义字母表,我们将退回到简单的数字符文比较。

如果两个输入字符串开头的两个符文相等,我们将继续处理每个输入字符串中的下一个符文。我们可以在对输入字符串进行切片时这样做:切片不会生成副本,它只返回一个指向原始字符串数据的新字符串头。

好的,现在让我们看看这个less()函数的实现:

func less(s1, s2 string) bool {
    for {
        switch e1, e2 := len(s1) == 0, len(s2) == 0; {
        case e1 && e2:
            return false // Both empty, they are equal (not less)
        case !e1 && e2:
            return false // s1 not empty but s2 is: s1 is greater (not less)
        case e1 && !e2:
            return true // s1 empty but s2 is not: s1 is less
        }

        r1, size1 := utf8.DecodeRuneInString(s1)
        r2, size2 := utf8.DecodeRuneInString(s2)

        // Check if both are custom, in which case we use custom order:
        custom := false
        if w1, ok1 := weights[r1]; ok1 {
            if w2, ok2 := weights[r2]; ok2 {
                custom = true
                if w1 != w2 {
                    return w1 < w2
                }
            }
        }
        if !custom {
            // Fallback to numeric rune comparison:
            if r1 != r2 {
                return r1 < r2
            }
        }

        s1, s2 = s1[size1:], s2[size2:]
    }
}

让我们看看这个less()函数的一些简单测试:

pairs := [][2]string{
    {"b", "c"},
    {"c", "a"},
    {"b", "a"},
    {"a", "b"},
    {"bca", "bac"},
}
for _, pair := range pairs {
    fmt.Printf("\"%s\" < \"%s\" ? %t\n", pair[0], pair[1], less(pair[0], pair[1]))
}

输出(在运动场上尝试):

"b" < "c" ? true
"c" < "a" ? true
"b" < "a" ? true
"a" < "b" ? false
"bca" < "bac" ? true

现在让我们在实际排序中测试这个less()函数:

ss := []string{
    "abc",
    "abca",
    "abcb",
    "abcc",
    "bca",
    "cba",
    "bac",
}
sort.Slice(ss, func(i int, j int) bool {
    return less(ss[i], ss[j])
})
fmt.Println(ss)

输出(在运动场上尝试):

[bca bac cba abc abcb abcc abca]

同样,如果性能对您很重要,则不应使用sort。Slice()因为它必须使用引擎盖下的反射,而是创建自己的切片类型来实现排序。接口,并且在您的实现中,您可以知道如何在不使用反射的情况下实现它。

这就是它的样子:

type CustStrSlice []string

func (c CustStrSlice) Len() int           { return len(c) }
func (c CustStrSlice) Less(i, j int) bool { return less(c[i], c[j]) }
func (c CustStrSlice) Swap(i, j int)      { c[i], c[j] = c[j], c[i] }

如果要使用自定义字母表对字符串片段进行排序,只需将片段转换为CustStrSlice,即可将其直接传递到sort。Sort()(此类型转换不会复制切片或其元素,只会更改类型信息):

ss := []string{
    "abc",
    "abca",
    "abcb",
    "abcc",
    "bca",
    "cba",
    "bac",
}
sort.Sort(CustStrSlice(ss))
fmt.Println(ss)

再次输出上述内容(在运动场上尝试):

[bca bac cba abc abcb abcc abca]

一些需要注意的事情:

默认字符串比较按字节比较字符串。也就是说,如果输入字符串包含无效的UTF-8序列,则仍将使用实际字节。

我们的解决方案在这方面是不同的,因为我们解码符文(我们必须这样做,因为我们使用自定义字母表,其中我们允许符文不一定映射到UTF-8编码中的字节1对1)。这意味着如果输入不是有效的UTF-8序列,则行为可能与默认排序不一致。但是如果您的输入是有效的UTF-8序列,这将执行您期望的操作。

最后一个注意事项:

我们已经了解了如何对字符串切片进行自定义排序。如果我们有一个结构片(或结构片指针),排序算法(less()函数)可能是相同的,但是当比较切片的元素时,我们必须比较元素的字段,而不是结构元素本身。

假设我们有以下结构:

type Person struct {
    Name string
    Age  int
}

func (p *Person) String() string { return fmt.Sprint(*p) }

(添加了String()方法,因此我们将看到结构的实际内容,而不仅仅是它们的地址...)

假设我们希望使用Person元素的Name字段,在类型为[]*Person的切片上应用自定义排序。因此,我们只需定义此自定义类型:

type PersonSlice []*Person

func (p PersonSlice) Len() int           { return len(p) }
func (p PersonSlice) Less(i, j int) bool { return less(p[i].Name, p[j].Name) }
func (p PersonSlice) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }

就这些。其余部分相同,例如:

ps := []*Person{
    {Name: "abc"},
    {Name: "abca"},
    {Name: "abcb"},
    {Name: "abcc"},
    {Name: "bca"},
    {Name: "cba"},
    {Name: "bac"},
}
sort.Sort(PersonSlice(ps))
fmt.Println(ps)

输出(在运动场上尝试):

[{bca 0} {bac 0} {cba 0} {abc 0} {abcb 0} {abcc 0} {abca 0}]
 类似资料:
  • 问题内容: 我正在使用ElasticSearch 2.4.2(通过Java的HibernateSearch 5.7.1.Final)。 我对字符串排序有问题。我的应用程序的语言带有变音符号,它们具有特定的字母顺序。例如,直接在after之后,在after之后,等等。因此,您应该对字符串进行如下排序: ElasticSearch首先按典型字母排序,然后将所有奇怪的字母移到最后: 我可以为Elasti

  • 问题内容: 我想为汽车清单开发一个排序演示。我正在使用数据表显示汽车列表。现在实际上我想按汽车颜色对列表进行排序。这里不是按字母顺序排序的。我想使用我的自定义排序顺序,例如先是红色汽车,然后是蓝色,等等。 为此,我尝试使用,但它只允许按字母顺序排序。 因此,任何人都可以指导我实现使用该技术的方法,以便使排序变得更快。 问题答案: 我建议你为汽车颜色创建一个枚举,而不要使用字符串,并且枚举的自然顺序

  • 根据字典顺序和自定义顺序对字符串数组进行排序(一种排列形式)。这是代码: 问题是,当我对一些输入运行此命令时,输出是正确的,而对于其他输入,输出是不正确的。我一直在调试它,但没有找到错误。 编辑: 阿德里安娜正在玩英文字母表。当她玩完字母表后,她意识到她把字母的位置弄乱了。现在,给定一组单词,她想知道根据她制作的新字母表排序,这些单词的字典排序是什么。 换句话说,给定英语字母表E和一组单词S的排列

  • 问题内容: 我想按字母顺序比较上述两个字符串(在本例中为“ Project”,然后是“ Sunject”,因为“ P”在“ S”之前)。有谁知道如何用Java做到这一点? 问题答案: 可能需要或可能不需要。 如果需要本地化的字符串排序,请查看此链接。

  • 下面的代码片段适用于条件1,但不适用于条件2。

  • 我有字符串数组:15MB、12MB、1TB、1GB。我想通过遵循MB小于GB和TB的规则来对它们进行词典比较。所以最后我想得到:12MB,15MB,1GB,1TB。我找到了一个比较字母的方法: 我在考虑用数字和字母拆分字符串,但我如何用字母“MB”对它们进行排序。然后根据他们的数字。我是使用两个比较器还是其他什么?