Avito Quiz: Golang

    Привет! Сегодня продолжаем серию мини-квизов в нашем блоге. Этот выпуск (предыдущий здесь) будет посвящен языку Go — будем считать, что это разминка в преддверие GopherCon Russia 2018 (кстати, у нас на этой конференции будет стенд, и мы планируем несколько интересных активностей).


    Под катом — семь вопросов и пара пасхалок. Ответы на вопросы выложим апдейтом к посту в понедельник, 26.02. Если будете решать — кладите ответы под спойлер, чтобы не портить другим фана.


    Enjoy!
    UPD: пост обновлён, теперь ответы внутри!


    1. Что выведет этот код?


    package main
    import (
        "fmt"
    )
    
    func add(arr []int, v int) {
        arr = append(arr, v)
    }
    
    func main() {
        arr := make([]int, 0, 100000)
        fmt.Printf("%v %p\n", arr, &arr)
        add(arr, 10)
        fmt.Printf("%v %p\n", arr, &arr)
    }

    Варианты ответов:


    • Пустой массив до вызова add, массив из 1 элемента после. Адреса не совпадают.
    • Оба раза пустой массив. Адреса совпадают.
    • Пустой массив до вызова add, массив из 1 элемента после. Адреса совпадают.
    • Оба раза пустой массив. Адреса не совпадают.

    Правильный ответ

    Оба раза пустой массив. Адреса совпадают.


    2. Что выведет этот код?


    package main
    
    import "fmt"
    
    func main() {
        numbers := [10]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
        s1 := numbers[:5:8]
        fmt.Println(s1)
        s2 := numbers[::4]
        fmt.Println(s2)
    
    }

    Варианты ответов:


    • Массив чисел от 0 до 5, затем массив чисел от 0 до 9
    • Массив чисел от 0 до 4, затем массив чисел от 0 до 9
    • Не скомпилируется
    • Массив чисел от 0 до 4, затем пустой массив.

    Правильный ответ

    Не скомпилируется


    3. Допустим у вас следующая иерархия:


    src/
        package_1/
            p1_a.go
            p1_b.go
            package_2/
                p2_a.go
                package_3/
                    p3_sh.go
                    p3_bash.go
                    p3_zsh.go

    Каждый модуль верхнего уровня импортирует модуль нижнего уровня т.е. package_1 импортирует package_2, а тот в свою очередь package_3. В каждом файле объявлена init-функция след. содержания:


    func init () {
        fmt.Println(<название файла, например: p1_a.go>)
    }

    Что будет выведено на экран?


    Варианты ответов:


    • Ошибка компиляции: More than one file in package declares init function
    • Порядок не определён
    • p1_a.go
      p1_b.go
      p2_a.go
      p3_a.go
      p3_bash.go
      p3_sh.go
      p3_zsh.go
    • Функции будут вызваны в случайном порядке, но в соответствии с иерархии начиная с нижнего модуля (package_3) и заканчивая верхним (package_1)

    Правильный ответ
    Функции будут вызваны в случайном порядке, но в соответствии с иерархии начиная с нижнего модуля (package_3) и заканчивая верхним (package_1)

    4. Чему будут равны константы:


    const (
        _ = iota
        Avito
        OLX
        LetGo
        Craiglist = iota 
        eBay
    )

    Варианты ответов:


    • 1,2,3,4,5
    • Ошибка компиляции: incorrect constant declaration: iota used twice
    • 0, 1, 2, 3, 4
    • 1,2,3,0, 4

    Правильный ответ

    1,2,3,4,5


    5. Опытный программист Роб решил добавить модуль для парсинга данных в стандартную библиотеку своего языка. Одним из модулей парсера будет конечный автомат, войдя в поток, Роб набросал следующий код, что произойдёт в момент первой компиляции?


    package main
    
    type state func(x int) state
    
    func start(x int) state {
        if x == 0 {
            return middle
        } else {
            return end
        }
    }
    
    func middle(_ int) state {
        return end
    }
    
    func end(_ int) state {
        return start
    }
    
    func main() {
        state := start(0)
        state = state(1)
        state = state(2)
    }

    Варианты ответов:


    • Ошибка в коде: произойдёт ошибка во время компиляции "type 'state' used in self declaration"
    • В stdout ничего не выведется, но процесс завершится успешно
    • Runtime ошибка: возникнет бесконечная рекурсия

    Правильный ответ

    В stdout ничего не выведется, но процесс завершится успешно.


    6. Каким будет результат выполнения кода?


    package main
    
    import (
        "fmt"
    )
    
    func test() (x int) {
        defer func () {
            x++
        } ()
        x = 1
    
        return x
    }
    
    func main() {
        fmt.Println(test())
    }

    Варианты ответов:


    • 1
    • 2
    • Compile error

    Правильный ответ

    2


    7. Молодой китайский программист Jian Yang решил освоить Go для своего нового гео-стартапа. Начав с обычного Hello World, он решил слегка его модифицировать, дабы освоить конструкцию range… Каким будет результат выполнения кода?


    package main
    
    import (
        "fmt"
    )
    
    func main() {
        var rc, bc int
    
        str := "Hello 世界"
    
        for _ = range str {
            rc++
        }
        for _ = range []byte(str){
            bc++
        }
    
        fmt.Println(rc, bc)
    }

    Варианты ответов:


    • 8 8
    • 8 12
    • 8 14
    • 8 16

    Правильный ответ

    8 12




    Желаем хорошего дня и ждём ваших ответов в комментариях. Следующий выпуск Avito Quiz планируем сделать по PHP.

    Avito 241,96
    У нас живут ваши объявления
    Поделиться публикацией
    Комментарии 19
      +2
      Заголовок спойлера
      1 Оба раза пустой массив. Адреса совпадают.
      2 Не скомпилируется
      3 Функции будут вызваны в случайном порядке, но в соответствии с иерархии начиная с нижнего модуля (package_3) и заканчивая верхним (package_1)
      4 1,2,3,4,5
      5 В stdout ничего не выведется, но процесс завершится успешно
      6 2
      7 8 12
        +1
        Можно было просто ссылку на play.golang.org скинуть :)
        Попортил фан
          +1
          Небольшое дополнение по 3: в принципе, порядок вызова init-функций в рамках одного пакета строго не регламентирован, в спецификации написано:
          To ensure reproducible initialization behavior, build systems are encouraged to present multiple files belonging to the same package in lexical file name order to a compiler.
          Поэтому, при запуске мы увидем:
          p3_bash.go
          p3_sh.go
          p3_zsh.go
          p2_a.go
          p1_a.go
          p1_b.go

          +2
          Я, конечно же, (почти) всё уже проверил сам на play.golang.org, но вот мои оригинальные ответы:

          Скрытый текст
          1. Оба раза пустой массив. Адреса совпадают.
          2. Не скомпилируется (но я думал, что по причине того, что capacity не может быть меньше длины, а не потому что длина обязательна)
          3. Функции будут вызваны в случайном порядке, но в соответствии с иерархии начиная с нижнего модуля (package_3) и заканчивая верхним (package_1)
          4. 1,2,3,0,1 — кстати говоря, такого варианта у вас нет, видимо опечатка
          5. В stdout ничего не выведется, но процесс завершится успешно — но за такой код я бы наказывал
          6. 2
          7. 8 14 — строго говоря, программисты на Го вряд ли должны знать ширину китайских иероглифов в UTF-8. Тут скорее важно, что сами ответы будут разные при итерации по символам и по байтам.
            0
            Почти всё верно) по ответам:
            3. дополнение
            4. iota соответствует индексу ConstSpec внутри const-блока, поэтому второе использование в том же блоке не сбрасывает значение в 0.
            5. На этот пример вдохновил доклад Пайка :)
            8. Полностью согласен, главное знать в чём отличие)
            +1
            Скрытый текст
            1) передача слайса значением. Изменить данные мы конечно сможем, но длинну нет, а она 0.
            2) никогда не использовал тройную запись, могу только предположить, что размерность 4 будет меньше длинны здесь: [::4]
            3) конечно, иерархия должна присутствовать, иначе мы рискуем использовать подпакет, который не инициализирован.
            4) iota сбрасывается каждый блок const, а он тут один.
            5) в stdout, собственно, ничего и не выводим. Но это ж не причина для паники)
            6) тут и объяснять ничего не надо, достаточно заменить return x на return, для тех, кто не заметил именованные возвратные значения
            7) да кто их знает, сколько байт один иероглиф займет?)
              +1
              Изменить данные мы конечно сможем, но длинну нет, а она 0.

              Но, кстати, мы можем таки получить доступ к данным:
              fmt.Printf("%v %p\n", arr[:1], &arr)
            +3

            Спасибо, вот вам в благодарность тоже задачка:


            package main
            
            import "fmt"
            
            func main() {
                for i := 0; i < 3; i++ {
                    defer func() {
                        fmt.Println(i)
                    }()
                }
            }
              0
              Чтобы ваш код работал так, как возможно, предполагается (ну на первый взгляд), надо после инициализации цикла добавить i:=i, а ещё лучше вот так:

              package main
              
              import "fmt"
              
              func main() {
                  for i := 0; i < 3; i++ {
                      defer func(i int) {
                          fmt.Println(i)
                      }(i)
                  }
              }
                0
                Думаю в этом и фишка, что код работает не так как кажется на первый взгляд…
                  0
                  Как раз он работает именно так, как кажется на первый взгляд — печатает 3 раза одно и то же число :). Другой вопрос, что наверное в цикле defer вызывать – не лучшая идея :).
                  +1
                  А ещё лучше просто вот так:

                  package main
                  
                  import "fmt"
                  
                  func main() {
                      for i := 0; i < 3; i++ {
                              defer fmt.Println(i)
                      }
                  }
                  


                  Потому что аргументы для вызова функции вычисляются на момент вызова defer
                  +1
                  Пожалуйста) ждите след. поста с задачками для PHP)
                  0
                  Вопрос знатокам Go по именным возвращаемым значениям, вы можете мне указать где в спецификации описывается следующее поведение:
                  package main
                  
                  import (
                      "fmt"
                  )
                  
                  func test() (x int) {
                      x = 1
                      defer func () {
                          x++
                      } ()
                  
                      return 2
                  }
                  
                  func main() {
                      fmt.Println(test())
                  }
                  


                  Спасибо
                    0
                    Отложенные ф-ции выполняются после возвращения из основной, т.е. сначала будет выполнено return 2 (x = 2), а затем x++.
                      0
                      Спасибо за ответ. Я имел в виду где описывается поведение `return 2` == `x = 2; return x`?
                      0
                      golang.org/ref/spec#Return_statements
                      A «return» statement that specifies results sets the result parameters before any deferred functions are executed.

                    Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                    Самое читаемое