Rozmowy rekrutacyjne to stres zarówno dla pracodawcy, jak i dla kandydata. Jedyną rozsądną decyzją jest więc wcześniejsze przygotowanie się do spotkania — zwłaszcza że pytania zadawane na różnych poziomach doświadczenia powinny się różnić.
Niezależnie od tego, czy przygotowujesz się do prowadzenia rozmowy o Go, czy chcesz pokazać się jako programista na czasie — ta lista jest dla Ciebie. Dziś pomożemy Ci zebrać listę ciekawych pytań (po 10 na każdy poziom doświadczenia), które zadasz przyszłym programistom.
Te pytania i odpowiedzi przydadzą się obu stronom rozmowy rekrutacyjnej.
01
Go to język programowania o otwartym źródle stworzony przez Google. Znany jest też jako Golang. Ten język jest przeznaczony przede wszystkim do programowania systemowego.
02
Ponieważ Go jest językiem o otwartym źródle, bardzo łatwo tworzy się w nim proste, niezawodne i wydajne oprogramowanie.
03
Język Go stworzyli Robert Griesemer, Rob Pike i Ken Thompson. Powstał w Google Inc. w 2009 roku.
04
Programy w Go składają się z pakietów. Program uruchamia się w pakiecie main. Ten program używa pakietów o ścieżkach importu „fmt” i „math / rand”.
05
Język Go wspiera programowanie uniwersalne.
06
Tak! Go rozróżnia wielkość liter.
07
Literały łańcuchowe określają stałą łańcuchową powstałą przez połączenie ciągu znaków.
Są dwa rodzaje literałów łańcuchowych:
08
Obszar roboczy zawiera kod w Go. To hierarchia katalogów z trzema katalogami w korzeniu.
09
Zmienna środowiskowa GOPATH wskazuje lokalizację obszaru roboczego. Musisz ją ustawić, kiedy piszesz kod w Go.
10
type Orange struct { Quantity int }
func (o *Orange) Increase(n int) { o.Quantity += n }
func (o *Orange) Decrease(n int) { o.Quantity -= n }
func (o *Orange) String() string { return fmt.Sprintf("%v", o.Quantity) }
func main() { var orange Orange orange.Increase(10) orange.Decrease(5) fmt.Println(orange) }
To podchwytliwe pytanie, bo można pomyśleć, że chodzi o błędne ustawienie pola Quantity — ale zostanie ono ustawione na 5, zgodnie z oczekiwaniami. Prawdziwy problem, który łatwo przeoczyć, polega na tym, że metoda String() implementująca interfejs fmt.Stringer() nie zostanie wywołana przy wypisywaniu obiektu orange funkcją fmt.Println(), ponieważ String() nie jest zdefiniowana dla wartości, a jedynie dla wskaźnika:
var orange Orange orange.Increase(10) orange.Decrease(5) fmt.Println(orange)
// Output: {5}
orange := &Orange{} orange.Increase(10) orange.Decrease(5) fmt.Println(orange)
// Output: 5
To subtelne pytanie, ale poprawka jest prosta. Musisz nadpisać metodę String() dla wartości zamiast dla wskaźnika — wtedy zadziała zarówno dla wskaźników, jak i dla wartości:
func (o Orange) String() string { return fmt.Sprintf("%v", o.Quantity) }
01
Zalety / korzyści języka Go:
02
Lista wbudowanego wsparcia w Go:
03
Goroutine to funkcja, która zwykle działa współbieżnie z innymi funkcjami. Jeśli chcesz zatrzymać goroutine, przekazujesz do niej kanał sygnałowy, przez który wysyłasz wartość, gdy chcesz ją zatrzymać.
Goroutine regularnie odpytuje ten kanał i kończy działanie, gdy tylko wykryje sygnał.
Quit : = make (chan bool)
go func ( ) {
for {
select {
case <- quit:
return
default
// do other stuff
}
}
}()
// Do stuff
// Quit goroutine
Quit <- true
04
Żeby zapisać wiele łańcuchów w Go, trzeba użyć surowego literału łańcuchowego, w którym łańcuch jest ograniczony odwrotnymi apostrofami.
05
Instrukcja break służy do zakończenia pętli for lub instrukcji switch i przeniesienia wykonania do instrukcji następującej bezpośrednio po pętli for lub switchu.
06
Instrukcja continue pozwala pominąć resztę ciała pętli i natychmiast ponownie sprawdzić warunek przed kolejnym przebiegiem.
07
Instrukcja goto służy do przekazania sterowania do instrukcji oznaczonej etykietą.
08
Składnia pętli for w języku Go wygląda tak:
for [condition | ( init; condition; increment ) | Range]
{
statement(s);
}
09
Składnia tworzenia funkcji w Go wygląda tak:
func function_name( [parameter list] ) [return_types]
{
body of the function
}
10
Deklaracja zmiennej o typie statycznym służy temu, żeby kompilator miał jedną zmienną o danym typie i nazwie i nie musiał znać wszystkich szczegółów tej zmiennej do dalszego przetwarzania. Deklaracja zmiennej ma znaczenie tylko na etapie kompilacji — kompilator potrzebuje faktycznej deklaracji w momencie linkowania programu.
01
Dwie wartości zamienia się miejscami tak prosto:
a, b = b, a
Żeby zamienić miejscami trzy wartości, piszemy:
a, b, c = b, c, a
Operacja zamiany w Go jest wolna od efektów ubocznych. Przypisywane wartości są gwarantowanie zapisywane w zmiennych tymczasowych, zanim zacznie się właściwe przypisanie, więc kolejność przypisań nie ma znaczenia. Wynik operacji: a: = 1; b: = 2; a, b, a = b, a, b to nadal a = 2 i b = 1, bez ryzyka zmiany wartości a na nowo przypisaną. W wielu implementacjach algorytmów warto na tym polegać.
Na przykład funkcja, która odwraca slice liczb całkowitych:
func reverse(s []int) {
for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
s[i], s[j] = s[j], s[i]
}
}
func main() {
a := []int{1, 2, 3}
reverse(a)
fmt.Println(a)
// Output: [3 2 1]
}
02
Slice kopiujesz wbudowaną funkcją copy ():
a := []int{1, 2}; b := []int{3, 4}; check := a; copy(a, b); fmt.Println(a, b, check)
// Output: [3 4] [3 4] [3 4]
Tutaj zmienna check służy do przechowania referencji do pierwotnego opisu slice'a, żeby upewnić się, że rzeczywiście został skopiowany.
W kolejnym przykładzie operacja nie kopiuje natomiast zawartości slice'a, a jedynie jego opis:
a := []int{1, 2}; b := []int{3, 4}; check := a; a = b; fmt.Println(a, b, check)
// Output: [3 4] [3 4] [1 2]
Mapę kopiujesz, przechodząc po jej kluczach. Tak, niestety to najprostszy sposób na skopiowanie mapy w Go:
a := map[string]bool{"A": true, "B": true}; b := make(map[string]bool)
for key, value := range a { b[key] = value }
W poniższym przykładzie kopiowany jest tylko opis mapy:
a := map[string]bool{"A": true, "B": true}; b := map[string]bool{"C": true, "D": true}; check := a
a = b; fmt.Println(a, b, check)
// Output: map[C:true D:true] map[C:true D:true] map[A:true B:true]
Go nie ma wbudowanego mechanizmu kopiowania interfejsu. Nie, funkcja reflect.DeepCopy() nie istnieje.
03
Dwie struktury możesz porównać operatorem ==, tak jak inne proste typy. Upewnij się tylko, że nie zawierają slice'ów, map ani funkcji — w takim wypadku kod się nie skompiluje.
type Foo struct { A int
B string
C interface{} }
a := Foo{A: 1, B: "one", C: "two"}
b := Foo{A: 1, B: "one", C: "two"}
println(a == b)
// Output: true
type Bar struct { A []int }
a := Bar{A: []int{1}}; b := Bar{A: []int{1}}
println(a == b)
// Output: invalid operation: a == b (struct containing []int cannot be compared)
Dwa interfejsy możesz porównać operatorem ==, jeśli typy pod spodem są „proste” i identyczne. W przeciwnym razie kod wywoła panikę w czasie działania:
var a interface{}; var b interface{}
a = 10; b = 10
println(a == b)
// Output: true
a = []int{1}; b = []int{2}
println(a == b)
// Output: panic: runtime error: comparing uncomparable type []int
Zarówno struktury, jak i interfejsy zawierające mapy i slice'y (ale nie funkcje) można porównywać funkcją reflect.DeepEqual():
var a interface{}; var b interface{}
a = []int{1}; b = []int{1}
println(reflect.DeepEqual(a, b))
// Output: true
a = map[string]string{"A": "B"}
b = map[string]string{"A": "B"}
println(reflect.DeepEqual(a, b))
// Output: true
temp := func() {}; a = temp; b = temp
println(reflect.DeepEqual(a, b))
// Output: false
Pakiet bytes ma przydatne funkcje pomocnicze do porównywania slice'ów bajtów: bytes.Equal(), bytes.Compare() i bytes.EqualFold(). Ta ostatnia służy do porównywania tekstu bez rozróżniania wielkości liter i jest znacznie szybsza niż reflection.DeepEqual().
04
Składnię języka GO opisuje się w rozszerzonej notacji Backusa-Naura (EBNF):
05
W Go interfejsy są sposobem na zdefiniowanie zachowania obiektu. Interfejs tworzy się słowem „type”, po którym następuje nazwa i słowo kluczowe interface. Interfejs definiują dwie rzeczy.
06
Asercja typu bierze wartość interfejsu i pobiera z niej wartość wskazanego, jawnego typu.
Konwersja typów służy w Golangu do zamiany typów różniących się od siebie.
07
W języku Go istnieje kilka różnych rodzajów funkcji zwanych metodami. W składni deklaracji metody „getter” oznacza kontener funkcji. Ten odbiornik pozwala wywołać funkcję za pomocą operatora „.”.
08
Język Go ma specjalny rodzaj instrukcji switch do sprawdzania typu zmiennej w czasie działania programu. Nazywa się go type switch.
09
Zmienne globalne nie są zalecane, ponieważ może do nich sięgać wiele goroutine (wątków) jednocześnie, co łatwo prowadzi do nieoczekiwanych zachowań i przypadkowych wyników.
10
Programowanie modularne to sposób podziału programu na podprogramy (moduły / funkcje) dla maksymalnej wydajności.
Definiowanie bardziej ogólnych funkcji ułatwia ich ponowne wykorzystanie — tak jak w przypadku wbudowanych funkcji bibliotecznych.
W naszej sieci talentów są setki sprawdzonych w boju ekspertów od tworzenia oprogramowania.
Jesteś programistą Go/Golang i szukasz świetnych projektów? Dołącz jako specjalista
Dane rejestrowe:
Firma
Usługi
Najczęściej poszukiwani specjaliści