Golangでreduce関数を提供しているライブラリugoを眺めてみた

## 1. やりたいこと

Golangって、foldとかreduceってできないのかな。。ということで[awsome-go](https://github.com/avelino/awesome-go)を眺めていたら見つけました。

* https://github.com/alxrm/ugo

どうやって実装しているのかな、というのが、下記のrob pikeさんのリポジトリとの対比もあって気になりました。

* https://godoc.org/robpike.io/filter
* https://github.com/robpike/filter

簡単な例として、数値のリストを受け取って、それを与えられた加算の関数を利用して、サムアップする、という処理を書いてみました。

## 2. ugoの利用方法と実装方法

### 2-1. ugoの利用方法

ugoを利用する場合には、下記のようになりました。
`my_add`という関数を事前に準備します。`my_func`の戻り値を、`ugo`の`Object`にしてあげる必要があります。
`Reduce`関数を利用して、リストの値を`my_add`でサムアップした後に、`ugo.Object`が返却されるので、例えば、リストの要素が`int`であれば、`.(int)`のようにしてあげる必要があるみたいです。

```go
package main

import "github.com/alxrm/ugo"

func my_add(elem, acc, _, _ ugo.Object) ugo.Object {
	return elem.(int) + acc.(int)
}

func main() {
	a := ugo.Seq{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
	result := ugo.Reduce(a, my_add, 1).(int)

	println(result)
}
```

### 2-2. ugoの実装方法

下記抜粋ですが、中身の実態です。

アキュミュレータの初期値、`initial`が`nil`の時などには、初期値をリストの先頭にしてくれるあたり、よしなにやってくれている感じがあります。
`Reduce`関数自体が返却するのは、`ugo.Object`になります。

```go
// Reduce makes single value from all of the slice elements, iterating from left
func Reduce(seq Seq, cb Collector, initial Object) Object {
	var memo Object

	if IsEmpty(seq) || cb == nil {
		return nil
	}

	length := len(seq) - 1

	if initial == nil {
		memo = seq[0]
		return createReduce(seq, cb, memo, 1, toMax, length-1)
	}

	memo = initial
	return createReduce(seq, cb, memo, 0, toMax, length)
}

// createShuffle returns single value folded to it from given slice
func createReduce(seq Seq, cb Collector, memo Object, startPoint, direction, length int) Object {
	result := memo
	index := startPoint

	for i := 0; i <= length; i++ {
		result = cb(result, seq[index], index, seq)
		index += direction
	}

	return result
}
```


## 3. rob pikeさんの利用方法と実装方法

### 3-1. robpike.io/filterの利用方法

次に、robpikeさんのReduceを利用してみました。

```go
package main

import "robpike.io/filter"
`ugo`とは違って、独自の`Object`を定義しておらず、シンプルにそのまま、`my_add`の戻り値の型を`int`にできるようです。利用者からすると、`ugo`よりはわかりやすそうです。

func my_add(elem, acc int) int { return elem + acc }

func main() {
	a := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
	result := filter.Reduce(a, my_add, 1).(int)

	println(result)
}
```

### 3-2. robpike.io/filterの実装方法

中身の実装を見てみると下記のようになっていました。
`reflect`パッケージを利用しています。
`Reduce`自体が返却するのは`interface{}`になっています。

```reduce.go
func Reduce(slice, pairFunction, zero interface{}) interface{} {
	in := reflect.ValueOf(slice)
	if in.Kind() != reflect.Slice {
		panic("reduce: not slice")
	}
	n := in.Len()
	switch n {
	case 0:
		return zero
	case 1:
		return in.Index(0)
	}
	elemType := in.Type().Elem()
	fn := reflect.ValueOf(pairFunction)
	if !goodFunc(fn, elemType, elemType, elemType) {
		str := elemType.String()
		panic("apply: function must be of type func(" + str + ", " + str + ") " + str)
	}
	// Do the first two by hand to prime the pump.
	var ins [2]reflect.Value
	ins[0] = in.Index(0)
	ins[1] = in.Index(1)
	out := fn.Call(ins[:])[0]
	// Run from index 2 to the end.
	for i := 2; i < n; i++ {
		ins[0] = out
		ins[1] = in.Index(i)
		out = fn.Call(ins[:])[0]
	}
	return out.Interface()
}
```

そして、rob pikeさんの実装だと、リストの要素数が1つだけの時は、エラーになってしまいます。
ここはもうちょっと見ていこうと思います。

とはいえ、このライブラリ自体をrob pikeさんも[README](https://github.com/robpike/filter/blob/master/README.md)で下記のように言っており、今後もGoではReduceが標準ライブラリになることはないかもしれません＾＾；

```
I wanted to see how hard it was to implement this sort of thing in Go, with as nice an API as I could manage. It wasn't hard.

Having written it a couple of years ago, I haven't had occasion to use it once. Instead, I just use "for" loops.

You shouldn't use it either.
```

---

本日は以上となります。