Помогите с Go(Golang)

Discussion in 'С/С++, C#, Rust, Swift, Go, Java, Perl, Ruby' started by AntiduPb, 29 Nov 2016.

  1. AntiduPb

    AntiduPb Member

    Joined:
    27 Apr 2013
    Messages:
    95
    Likes Received:
    90
    Reputations:
    0
    Привет всем! Есть тут те кто могёт в Go?
    Есть такая задачка:


    Измените программу так, чтобы цифры от 1 до 9 печатались в консоль по порядку (Golang). Разрешено внести изменения в участки кода, помеченные комментарием «// redact».

    Code:
    package main
    
    import "time"
    
    func main() {
        // redact
        go func() {
            for _, value := range []int{1, 4, 7} {
                // redact
                println(value)
                // redact
            }
        }()
    
        go func() {
            for _, value := range []int{2, 5, 8} {
                // redact
                println(value)
                // redact
            }
        }()
    
        go func() {
            for _, value := range []int{3, 6, 9} {
                // redact
                println(value)
                // redact
            }
        }()
    
        time.Sleep(time.Second)
    }
    
    Я решил эту задачу так:

    Code:
    package main
    
    import "time"
    
    func main() {
        preOutValue := make(chan int, 1) // Создаем канал
        defer close(preOutValue)         // Не закрывать канал до завершения main()
        preOutValue <- 1                 // Закидываем в канал значание 1 типа int
        go func() {
            for _, value := range []int{1, 4, 7} {
    
                for {
                    tmpval1 := <-preOutValue // Достаем значение из канала в переменную tmpval1
                    if tmpval1 == value {    // если полученное значение соответствует value
                        println(value)           // Выводим value
                        preOutValue <- value + 1 // Отправляем в канал значение на 1 больше
                        break
                    } else {
                        preOutValue <- tmpval1 // Если сообщение из канала не совпало с нашим, закинем его обратно
                    }
                }
    
            }
        }()
    
        go func() {
            for _, value := range []int{2, 5, 8} {
    
                for {
                    tmpval2 := <-preOutValue
                    if tmpval2 == value {
                        println(value)
                        preOutValue <- value + 1
                        break
                    } else {
                        preOutValue <- tmpval2
                    }
                }
    
            }
        }()
    
        go func() {
            for _, value := range []int{3, 6, 9} {
    
                for {
                    tmpval3 := <-preOutValue
                    if tmpval3 == value {
                        println(value)
                        preOutValue <- value + 1
                        break
                    } else {
                        preOutValue <- tmpval3
                    }
                }
            }
        }()
    
        time.Sleep(time.Second)
    }
    
    Но мне говорят что это неправильно, а как правильно не сказали.
    Кто знает как правильно?

    P.S. Базово изучил язык за 1-ну ночь, так что PLZ сильно не пинать.
     
  2. crlf

    crlf Green member

    Joined:
    18 Mar 2016
    Messages:
    683
    Likes Received:
    1,513
    Reputations:
    460
    Не знаком с go, но по логике просится sleep. Тормозя итерацию в каждом блоке (time.Sleep(time.Millisecond * X)), можно добиться последовательной выдачи результатов.
     
  3. AntiduPb

    AntiduPb Member

    Joined:
    27 Apr 2013
    Messages:
    95
    Likes Received:
    90
    Reputations:
    0
    Да нет, вопрос в том что мой код(моё решение) выдает правильный результат, мне просто сказали что сам код написан неправильно(типа быдлокод), а почему не сказали :(
     
  4. scrat

    scrat кодер

    Joined:
    8 Apr 2007
    Messages:
    625
    Likes Received:
    541
    Reputations:
    3
    Привет,

    Сначала по твоему решению:
    Code:
        defer close(preOutValue)         // Не закрывать канал до завершения main()
    
    В комментарии ты неправильно описываешь логику работы defer: https://golang.org/doc/effective_go.html#defer

    Далее тут:
    Code:
    for {
        tmpval1 := <-preOutValue // Достаем значение из канала в переменную tmpval1
        if tmpval1 == value {    // если полученное значение соответствует value
            println(value)           // Выводим value
            preOutValue <- value + 1 // Отправляем в канал значение на 1 больше
            break
        } else {
            preOutValue <- tmpval1 // Если сообщение из канала не совпало с нашим, закинем его обратно
        }
    }
    
    Теоретически может возникнуть ситуация, когда две из трёх горутин повиснут на этом канале и третяя никогда не сработает. Такая, пусть и маловероятная, ситуация делает твой код некорректным.

    Ещё этот for-цикл не выглядит очень изящным в то время как язык предоставляет тебе уйму средств для асинхронного программирования и синхронизации.

    Далее пара изменённых версий этого же задания:

    Code:
    package main
    
    import "time"
    
    func main() {
        c1, c2, c3 := make(chan bool, 1), make(chan bool, 1), make(chan bool, 1)
        defer close(c1)
        defer close(c2)
        defer close(c3)
    
        c1 <- true
    
        go func() {
            for _, value := range []int{1, 4, 7} {
                <-c1
                println(value)
                c2 <- true
            }
        }()
    
        go func() {
            for _, value := range []int{2, 5, 8} {
                <-c2
                println(value)
                c3 <- true
            }
        }()
    
        go func() {
            for _, value := range []int{3, 6, 9} {
                <-c3
                println(value)
                c1 <- true
            }
        }()
    
        time.Sleep(time.Second)
    }
    

    Code:
    package main
    
    import "time"
    
    func main() {
        go func() {
            for _, value := range []int{1, 4, 7} {
                for i := 1; i < 10; i++ {
                    println(i)
                }
                break
                println(value)
            }
        }()
    
        go func() {
            for _, value := range []int{2, 5, 8} {
                break
                println(value)
            }
        }()
    
        go func() {
            for _, value := range []int{3, 6, 9} {
                break
                println(value)
            }
        }()
    
        time.Sleep(time.Second)
    }
    
     
    crlf and AntiduPb like this.
  5. AntiduPb

    AntiduPb Member

    Joined:
    27 Apr 2013
    Messages:
    95
    Likes Received:
    90
    Reputations:
    0
    Т.е. тут, фактически, когда дело доходит до "<-c1" , поток залипает в ожидании числа из канала? Т.е. это, как бы, работа по событию? И когда потоки запустились, они фактически стоят и ждут пока что-то не прилетит из канала? Я правильно понял?