Avatar of artemkorsakov

artemkorsakov's solution

to List Ops in the Go Track

Published at Feb 06 2019 · 0 comments
Instructions
Test suite
Solution

Implement basic list operations.

In functional languages list operations like length, map, and reduce are very common. Implement a series of basic list operations, without using existing functions.

Running the tests

To run the tests run the command go test from within the exercise directory.

If the test suite contains benchmarks, you can run these with the --bench and --benchmem flags:

go test -v --bench . --benchmem

Keep in mind that each reviewer will run benchmarks on a different machine, with different specs, so the results from these benchmark tests may vary.

Further information

For more detailed information about the Go track, including how to get help if you're having trouble, please visit the exercism.io Go language page.

Submitting Incomplete Solutions

It's possible to submit an incomplete solution so you can see how others have completed the exercise.

listops_test.go

package listops

import "reflect"
import "testing"

var foldTestCases = []struct {
	name     string
	property string
	fn       binFunc
	initial  int
	list     IntList
	want     int
}{
	{
		name:     "empty list",
		property: "foldl",
		fn:       func(x, y int) int { return x * y },
		initial:  2,
		want:     2,
		list:     []int{},
	},
	{
		name:     "direction independent function applied to non-empty list",
		property: "foldl",
		fn:       func(x, y int) int { return x + y },
		initial:  5,
		want:     15,
		list:     []int{1, 2, 3, 4},
	},
	{
		name:     "direction dependent function applied to non-empty list",
		property: "foldl",
		fn:       func(x, y int) int { return x / y },
		initial:  5,
		want:     0,
		list:     []int{2, 5},
	},
	{
		name:     "empty list",
		property: "foldr",
		fn:       func(x, y int) int { return x * y },
		initial:  2,
		want:     2,
		list:     []int{},
	},
	{
		name:     "direction independent function applied to non-empty list",
		property: "foldr",
		fn:       func(x, y int) int { return x + y },
		initial:  5,
		want:     15,
		list:     []int{1, 2, 3, 4},
	},
	{
		name:     "direction dependent function applied to non-empty list",
		property: "foldr",
		fn:       func(x, y int) int { return x / y },
		initial:  5,
		want:     2,
		list:     []int{2, 5},
	},
}

func TestFold(t *testing.T) {
	var got int
	for _, tt := range foldTestCases {
		if tt.property == "foldr" {
			got = tt.list.Foldr(tt.fn, tt.initial)
		} else {
			got = tt.list.Foldl(tt.fn, tt.initial)
		}
		if got != tt.want {
			t.Fatalf("FAIL: %s: %q -- expected: %d, actual: %d", tt.property, tt.name, tt.want, got)
		} else {
			t.Logf("PASS: %s: %s", tt.property, tt.name)
		}

	}

}

var filterTestCases = []struct {
	name     string
	property string
	fn       predFunc
	list     []int
	want     []int
}{
	{
		name:     "empty list",
		property: "filter",
		fn:       func(n int) bool { return n%2 == 1 },
		list:     []int{},
		want:     []int{},
	},
	{
		name:     "non-empty list",
		property: "filter",
		fn:       func(n int) bool { return n%2 == 1 },
		list:     []int{1, 2, 3, 4, 5},
		want:     []int{1, 3, 5},
	},
}

func TestFilterMethod(t *testing.T) {
	for _, tt := range filterTestCases {
		in := IntList(tt.list)
		got := in.Filter(tt.fn)
		if !reflect.DeepEqual(IntList(tt.want), got) {
			t.Fatalf("FAIL: %s: %q -- expected: %v, actual: %v", tt.property, tt.name, tt.want, got)
		} else {
			t.Logf("PASS: %s: %s", tt.property, tt.name)
		}

	}
}

var lengthTestCases = []struct {
	name     string
	property string
	list     IntList
	want     int
}{
	{
		name:     "empty list",
		property: "length",
		list:     []int{},
		want:     0,
	},
	{
		name:     "non-empty list",
		property: "length",
		list:     []int{1, 2, 3, 4},
		want:     4,
	},
}

func TestLengthMethod(t *testing.T) {
	for _, tt := range lengthTestCases {
		got := tt.list.Length()
		if tt.want != got {
			t.Fatalf("FAIL: %s: %q -- expected: %d, actual: %d", tt.property, tt.name, tt.want, got)
		} else {
			t.Logf("PASS: %s: %s", tt.property, tt.name)
		}

	}
}

var mapTestCases = []struct {
	name     string
	property string
	list     IntList
	fn       unaryFunc
	want     IntList
}{
	{
		name:     "empty list",
		property: "map",
		list:     []int{},
		fn:       func(x int) int { return x + 1 },
		want:     []int{},
	},
	{
		name:     "non-empty list",
		property: "map",
		list:     []int{1, 3, 5, 7},
		fn:       func(x int) int { return x + 1 },
		want:     []int{2, 4, 6, 8},
	},
}

func TestMapMethod(t *testing.T) {
	for _, tt := range mapTestCases {
		got := tt.list.Map(tt.fn)
		if !reflect.DeepEqual(tt.want, got) {
			t.Fatalf("FAIL: %s: %q -- expected: %v, actual: %v", tt.property, tt.name, tt.want, got)
		} else {
			t.Logf("PASS: %s: %s", tt.property, tt.name)
		}

	}
}

var reverseTestCases = []struct {
	name     string
	property string
	list     IntList
	want     IntList
}{
	{
		name:     "empty list",
		property: "reverse",
		list:     []int{},
		want:     []int{},
	},
	{
		name:     "non-empty list",
		property: "reverse",
		list:     []int{1, 3, 5, 7},
		want:     []int{7, 5, 3, 1},
	},
}

func TestReverseMethod(t *testing.T) {
	for _, tt := range reverseTestCases {
		got := tt.list.Reverse()
		if !reflect.DeepEqual(tt.want, got) {
			t.Fatalf("FAIL: %s: %q -- expected: %v, actual: %v", tt.property, tt.name, tt.want, got)
		} else {
			t.Logf("PASS: %s: %s", tt.property, tt.name)
		}

	}
}

var appendTestCases = []struct {
	name       string
	property   string
	list       IntList
	appendThis IntList
	want       IntList
}{
	{
		name:       "empty list",
		property:   "append",
		list:       []int{},
		appendThis: []int{},
		want:       []int{},
	},
	{
		name:       "empty list to list",
		property:   "append",
		list:       []int{},
		appendThis: []int{1, 2, 3, 4},
		want:       []int{1, 2, 3, 4},
	},
	{
		name:       "non-empty lists",
		property:   "append",
		list:       []int{1, 2},
		appendThis: []int{2, 3, 4, 5},
		want:       []int{1, 2, 2, 3, 4, 5},
	},
}

func TestAppendMethod(t *testing.T) {
	for _, tt := range appendTestCases {
		got := tt.list.Append(tt.appendThis)
		if !reflect.DeepEqual(tt.want, got) {
			t.Fatalf("FAIL: %s: %q -- expected: %v, actual: %v", tt.property, tt.name, tt.want, got)
		} else {
			t.Logf("PASS: %s: %s", tt.property, tt.name)
		}

	}
}

var concatTestCases = []struct {
	name     string
	property string
	list     IntList
	args     []IntList
	want     IntList
}{
	{
		name:     "empty list",
		property: "concat",
		list:     []int{},
		args:     []IntList{},
		want:     []int{},
	},
	{
		name:     "list of lists",
		property: "concat",
		list:     []int{1, 2},
		args:     []IntList{[]int{3}, []int{}, []int{4, 5, 6}},
		want:     []int{1, 2, 3, 4, 5, 6},
	},
}

func TestConcatMethod(t *testing.T) {
	for _, tt := range concatTestCases {
		got := tt.list.Concat(tt.args)
		if !reflect.DeepEqual(tt.want, got) {
			t.Fatalf("FAIL: %s: %q -- expected: %v, actual: %v", tt.property, tt.name, tt.want, got)
		} else {
			t.Logf("PASS: %s: %s", tt.property, tt.name)
		}
	}
}
package listops

// IntList implements basic list operations.
type IntList []int
type binFunc func(x, y int) int
type predFunc func(n int) bool
type unaryFunc func(x int) int

// Length returns list length.
func (r *IntList) Length() int {
	return len(*r)
}

// Append appends the given list to the current one.
func (r *IntList) Append(addList IntList) interface{} {
	for _, v := range addList {
		*r = append(*r, v)
	}
	return *r
}

// Concat concats the given array list to the current one.
func (r *IntList) Concat(listOfLists []IntList) interface{} {
	for i := range listOfLists {
		for _, v := range listOfLists[i] {
			*r = append(*r, v)
		}
	}
	return *r
}

// Filter filters the list according to the given function
func (r *IntList) Filter(fn predFunc) interface{} {
	curList := make([]int, 0)
	for _, v := range *r {
		if fn(v) {
			curList = append(curList, v)
		}
	}
	*r = curList
	return *r
}

// Map converts each element of the list according to the given function
func (r *IntList) Map(fn unaryFunc) interface{} {
	curList := make([]int, 0)
	for _, v := range *r {
		curList = append(curList, fn(v))
	}
	*r = curList
	return *r
}

// Reverse reverses list
func (r *IntList) Reverse() interface{} {
	resultList := make([]int, 0)
	curList := *r
	for i := range curList {
		el := curList[len(curList)-1-i]
		resultList = append(resultList, el)
	}
	*r = resultList
	return *r
}

func (r *IntList) Foldl(bf binFunc, init int) int {
	result := init
	for _, x := range *r {
		result = bf(result, x)
	}
	return result
}

func (r *IntList) Foldr(bf binFunc, init int) int {
	curList := *r
	result := init
	for i := len(*r) - 1; i >= 0; i-- {
		result = bf(curList[i], result)
	}
	return result
}

Community comments

Find this solution interesting? Ask the author a question to learn more.

What can you learn from this solution?

A huge amount can be learned from reading other people’s code. This is why we wanted to give exercism users the option of making their solutions public.

Here are some questions to help you reflect on this solution and learn the most from it.

  • What compromises have been made?
  • Are there new concepts here that you could read more about to improve your understanding?