使用Go语言中的映射实现高阶Fizz Buzz的方法是什么?

huangapple go评论75阅读模式
英文:

Implementing fizz buzz of higher order using maps in go lang?

问题

我正在尝试使用Go语言中的映射实现Fizz Buzz问题。然而,这段代码在工作中需要改进。由于for循环遍历映射,它会持续打印出不需要的和冗余的结果。我尝试了很多解决方案,但都失败了。在不使用键的切片的帮助下,这种改进可行吗?

package main

import "fmt"

func fizzbuzz(i int) {
    myMap := make(map[int]string)
    myMap[3] = "fizz"
    myMap[5] = "buzz"
    myMap[15] = "fizzbuzz"

    for k, v := range myMap {
        if i%k == 0 {
            fmt.Printf("%v \n", v)
        } else {
            fmt.Printf("%v \n", i)
        }
    }
}

func main() {

    for i := 1; i < 10000; i++ {
        fizzbuzz(i)
    }

}

请注意,我只翻译了你提供的代码部分,其他内容不包括在内。

英文:

I am trying to implement the fizz buzz problem using maps in go lang. However, this code requires improvement in its working. It keeps on printing undesired and redundant results due to the for loop that iterates over the map. I tried a lot of solutions but failed. Is it feasible without using any help of a slice of keys?

package main

import &quot;fmt&quot;

func fizzbuzz(i int)  {
	myMap:= make(map[int]string)
	myMap[3] = &quot;fizz&quot;
	myMap[5] = &quot;buzz&quot;
	myMap[15] = &quot;fizzbuzz&quot;

	for k,v:= range myMap{
		if i%k==0 {fmt.Printf(&quot;%v \n&quot;,v)
		} else {fmt.Printf(&quot;%v \n&quot;,i)}
	}
}

func main() {

	for i:=1;i&lt;10000;i++ {
		fizzbuzz(i)
	}

}

答案1

得分: 1

使用映射

根据你的规则集,整个for循环应该用于决定是否用一个单词替换i数值。但是你在每次迭代中都发出了一个结果。for最多应该发出一个结果。如果i不能被任何键整除,则应该发出i

键可以是其他键的倍数(例如15 = 3 * 5),如果i可以被这样的键整除,我们希望发出与最大键关联的单词。因此,for循环不应该发出任何内容,因为如果你找到一个好的键,可能还有一个更大的键。所以循环只需找到最大的好键。

在循环之后,你可以检查是否找到了任何好的键,如果找到了,就发出与之关联的单词,否则发出该数字:

var rules = map[int]string{
	3:  "fizz",
	5:  "buzz",
	15: "fizzbuzz",
}

func fizzbuzz(i int) {
	max := -1
	for k := range rules {
		if i%k == 0 && k > max {
			max = k
		}
	}

	if max < 0 {
		fmt.Println(i)
	} else {
		fmt.Println(rules[max])
	}
}

func main() {
	for i := 1; i < 100; i++ {
		fizzbuzz(i)
	}
}

输出结果(在[Go Playground][1]上尝试):

1
2
fizz
4
buzz
fizz
7
8
fizz
buzz
11
fizz
13
14
fizzbuzz
16
17
fizz
19
buzz
fizz
...

### 使用有序切片

如果按键降序排序规则,可以获得更好的性能,这样你可以按照这个顺序(从大到小)检查键,然后第一个符合条件的键将是最大的。因此,你可以立即发出结果并返回。

如果循环后继续执行,我们知道没有好的键,我们可以发出`i`数值:

```go
var rules = []struct {
	n    int
	word string
}{
	{15, "fizzbuzz"},
	{5, "buzz"},
	{3, "fizz"},
}

func fizzbuzz(i int) {
	for _, rule := range rules {
		if i%rule.n == 0 {
			fmt.Println(rule.word)
			return
		}
	}

	fmt.Println(i)
}

在[Go Playground][2]上尝试。

### 通用方法(排除规则中的倍数)

尽管你最初的规则集中包含了`15 = 3 * 5`,但实际上不应该这样;你只需要列出`3`和`5`,`15`应该是隐含的。

在这种情况下,你当然必须检查所有的规则,因为每个好的键都应该发出一个单词。你还必须记住是否找到了一个好的键,只有在找不到好的键时才发出`i`数值。

你可以这样做:

```go
var rules = []struct {
	n    int
	word string
}{
	{3, "fizz"},
	{5, "buzz"},
}

func fizzbuzz(i int) {
	found := false
	for _, rule := range rules {
		if i%rule.n == 0 {
			found = true
			fmt.Print(rule.word)
		}
	}
	if !found {
		fmt.Print(i)
	}
	fmt.Println()
}

在[Go Playground][3]上尝试。

注意:在这个解决方案中,你也可以使用映射而不是切片;我使用切片的原因是,如果有多个好的键,发出的单词将始终按照相同的顺序(由增加的键定义),因为映射中的键的迭代顺序未定义。有关详细信息,请参见https://stackoverflow.com/questions/28930416/why-cant-go-iterate-maps-in-insertion-order/28931555#28931555

  [1]: https://play.golang.org/p/xO2uVXqbBz
  [2]: https://play.golang.org/p/50efZx7Q0e
  [3]: https://play.golang.org/p/zPltHuGl-R

<details>
<summary>英文:</summary>

### With a map

With your rule set, the entire `for` loop should be to decide if the `i` number is to be replaced with a word. But you emit a result in each iteration. At most one result should be emitted by the `for`. If `i` is not dividable by any of the keys, then `i` should be emitted.

Keys may be multiples of others (e.g. `15 = 3 * 5`), and if the `i` number is dividable by such a key, we want to emit the word associated with the greatest key. So the `for` loop should not emit anything, because if you find a good key, there may be a greater one. So the loop should just find the greatest good key.

After the loop you can check if any good key was found, and if so, emit the word associated with it, else emit the number:

    var rules = map[int]string{
    	3:  &quot;fizz&quot;,
    	5:  &quot;buzz&quot;,
    	15: &quot;fizzbuzz&quot;,
    }
    
    func fizzbuzz(i int) {
    	max := -1
    	for k := range rules {
    		if i%k == 0 &amp;&amp; k &gt; max {
    			max = k
    		}
    	}
    
    	if max &lt; 0 {
    		fmt.Println(i)
    	} else {
    		fmt.Println(rules[max])
    	}
    }
    
    func main() {
    	for i := 1; i &lt; 100; i++ {
    		fizzbuzz(i)
    	}
    
    }

Output (try it on the [Go Playground][1]):

    1
    2
    fizz
    4
    buzz
    fizz
    7
    8
    fizz
    buzz
    11
    fizz
    13
    14
    fizzbuzz
    16
    17
    fizz
    19
    buzz
    fizz
    ...

### With an ordered slice

You can get better performance if the rules are sorted by the keys descending, in which case you can check the keys in that order (greatest first), and then the first that qualifies will be the greatest. So you can emit the result immediately, and return.

If execution continues after the loop, we know no keys were good, we can emit the `i` number:

    var rules = []struct {
    	n    int
    	word string
    }{
    	{15, &quot;fizzbuzz&quot;},
    	{5, &quot;buzz&quot;},
    	{3, &quot;fizz&quot;},
    }
    
    func fizzbuzz(i int) {
    	for _, rule := range rules {
    		if i%rule.n == 0 {
    			fmt.Println(rule.word)
    			return
    		}
    	}
    
    	fmt.Println(i)
    }

Try this on the [Go Playground][2].

### General (excluding multiples from rules)

Although you started with a rule set where `15 = 3 * 5` was included in the rules, this should not be the case; you should only list `3` and `5`, `15` should be implicit.

In this case, you have to check all the rules of course, because each good key should emit a word. And you have to remember if a good key was found, and only emit the `i` number otherwise.

This is how you can do it:

    var rules = []struct {
    	n    int
    	word string
    }{
    	{3, &quot;fizz&quot;},
    	{5, &quot;buzz&quot;},
    }
    
    func fizzbuzz(i int) {
    	found := false
    	for _, rule := range rules {
    		if i%rule.n == 0 {
    			found = true
    			fmt.Print(rule.word)
    		}
    	}
    	if !found {
    		fmt.Print(i)
    	}
    	fmt.Println()
    }

Try it on the [Go Playground][3].

Note: in this solution you could also use a map instead of the slice; the reason why I used a slice is so that in case of multiple good keys the emitted words will always be in the same order (defined by increasing keys), as iteration order of keys in a map is not defined. For details, see https://stackoverflow.com/questions/28930416/why-cant-go-iterate-maps-in-insertion-order/28931555#28931555

  [1]: https://play.golang.org/p/xO2uVXqbBz
  [2]: https://play.golang.org/p/50efZx7Q0e
  [3]: https://play.golang.org/p/zPltHuGl-R

</details>



# 答案2
**得分**: 0

如前所述,在Go语言中,映射中项目的顺序是不确定的。以下是一些简单的解决方案:

```go
func fizzbuzz(n int) {
    for i := 1; i <= n; i++ {
        switch {
        case i%15 == 0:
            println("fizzbuzz")
        case i%5 == 0:
            println("buzz")
        case i%3 == 0:
            println("fizz")
        default:
            println(i)
        }
    }
}

func fizzbuzzList(n int) []string {
    var res []string
    for i := 1; i <= n; i++ {
        switch {
        case i%15 == 0:
            res = append(res, "fizzbuzz")
        case i%5 == 0:
            res = append(res, "buzz")
        case i%3 == 0:
            res = append(res, "fizz")
        default:
            res = append(res, strconv.Itoa(i))
        }
    }
    return res
}

func fizzbuzzLazy(n int) chan string {
    var res = make(chan string)
    go func() {
        for i := 1; i <= n; i++ {
            switch {
            case i%15 == 0:
                res <- "fizzbuzz"
            case i%5 == 0:
                res <- "buzz"
            case i%3 == 0:
                res <- "fizz"
            default:
                res <- strconv.Itoa(i)
            }
        }
        close(res)
    }()
    return res
}

用法示例:

fizzbuzz(20)

for _, v := range fizzbuzzList(20) {
    println(v)
}

for v := range fizzbuzzLazy(20) {
    println(v)
}
英文:

As mentioned, the order of items in a map, is not deterministic in Go. Though here are some simple solutions:

func fizzbuzz(n int) {
	for i := 1; i &lt;= n; i++ {
		switch {
		case i%15 == 0:
			println(&quot;fizzbuzz&quot;)
		case i%5 == 0:
			println(`buzz`)
		case i%3 == 0:
			println(`fizz`)
		default:
			println(i)
		}
	}
}

func fizzbuzzList(n int) []string {
	var res []string
	for i := 1; i &lt;= n; i++ {
		switch {
		case i%15 == 0:
			res = append(res, `fizzbuzz`)
		case i%5 == 0:
			res = append(res, `buzz`)
		case i%3 == 0:
			res = append(res, `fizz`)
		default:
			res = append(res, strconv.Itoa(i))
		}
	}
	return res
}

func fizzbuzzLazy(n int) chan string {
	var res = make(chan string)
	go func() {
		for i := 1; i &lt;= n; i++ {
			switch {
			case i%15 == 0:
				res &lt;- `fizzbuzz`
			case i%5 == 0:
				res &lt;- `buzz`
			case i%3 == 0:
				res &lt;- `fizz`
			default:
				res &lt;- strconv.Itoa(i)
			}
		}
		close(res)
	}()
	return res
}

And usage:

fizzbuzz(20)

for _, v := range fizzbuzzList(20) {
	println(v)
}

for v := range fizzbuzzLazy(20) {
	println(v)
}

huangapple
  • 本文由 发表于 2017年2月16日 15:40:06
  • 转载请务必保留本文链接:https://go.coder-hub.com/42267715.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定