How to generate exam questions

Martin Sulzmann

Things are changing

In the past:

Don’t memorize exams — understand how exams are constructed.

“Model-based” elective course

Suppose the focus of the exam is on Go.

Here are a few questions/answers that can be extracted from the lecture notes and have been discussed during the lecture.

Please generate exam-style questions for an exam that covers Go.

Here are the lecture notes.

Go basics https://sulzmann.github.io/ModelBasedSW/mbsw-go-basics.html#(1) Go methods, interfaces, generics https://sulzmann.github.io/ModelBasedSW/mbsw-go-advanced.html#(1) Further examples https://sulzmann.github.io/ModelBasedSW/mbsw-go-details.html#(1) Translating Go https://sulzmann.github.io/ModelBasedSW/mbsw-go-translate.html#(1)

Here are some examples of questions/answers ....

Below I collect examples obtained via ChatGPT.

Slices vs Arrays (classic trap)

// QUESTION.
// What value will be printed out onto the console?
// ANSWER.
// prints 1. Arrays are passed by value.
func h1() {
    xs := [2]int{1, 1}

    inc := func(ys [2]int) { ys[0]++ }

    inc(xs)
    fmt.Printf("%d", xs[0])
}

Pointer semantics

// QUESTION.
// What value will be printed out onto the console?
// ANSWER.
// prints 2. Pointer allows mutation of original value.
func h2() {
    x := 1

    inc := func(p *int) { *p = *p + 1 }

    inc(&x)
    fmt.Printf("%d", x)
}

Closure capturing variable

// QUESTION.
// What value will be printed out onto the console?
// ANSWER.
// prints 2. Closures capture variables, not values.
func h3() {
    x := 1

    f := func() { x++ }

    f()
    fmt.Printf("%d", x)
}

Loop variable capture (important Go pitfall)

// QUESTION.
// What values will be printed out onto the console?
// ANSWER.
// prints 3 3 3. The loop variable i is captured by reference.
func h4() {
    fs := []func(){}

    for i := 0; i < 3; i++ {
        fs = append(fs, func() { fmt.Printf("%d ", i) })
    }

    for _, f := range fs {
        f()
    }
}
// Fix the program so that it prints 0 1 2.
// ANSWER.
// Introduce local variable inside loop.

Method receiver semantics

type counter struct {
    x int
}

func (c counter) inc() {
    c.x++
}

// QUESTION.
// What value will be printed?
// ANSWER.
// prints 1. Receiver is passed by value.
func h6() {
    c := counter{1}
    c.inc()
    fmt.Printf("%d", c.x)
}

Generics constraint

type Adder interface {
    add(int) int
}

func apply[T Adder](x T) int {
    return x.add(1)
}

// QUESTION.
// Will this program type check?
// ANSWER.
// Yes, if type implements add(int) int.

Type inference with generics

func id[T any](x T) T {
    return x
}

// QUESTION.
// What is the type of id(1)?
// ANSWER.
// int. Type argument inferred automatically.

Slice append aliasing

// QUESTION.
// What values will be printed?
// ANSWER.
// prints 2. Underlying array is shared.
func h9() {
    xs := []int{1}
    ys := xs

    ys[0] = 2
    fmt.Printf("%d", xs[0])
}

Append subtlety

// QUESTION.
// What values will be printed?
// ANSWER.
// depends on capacity; may print 1 or 2.
// append may allocate new array.
func h10() {
    xs := []int{1}
    ys := append(xs, 2)

    ys[0] = 2
    fmt.Printf("%d", xs[0])
}

Structural typing

type S interface {
    f() int
}

type T2 struct{}

func (T2) f() int { return 42 }

// QUESTION.
// Why does assignment type check?
// ANSWER.
// Structural typing: T2 implements S implicitly.
func h11() {
    var x S = T2{}
    fmt.Println(x.f())
}