109
76

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Go言語でプログラマーが進化する過程

Posted at

はじめに

「The Evolution of a Go Programmer」こちらの解説がおもしろかったので、コードごとに解説しようと思います。
下記のコードは階乗計算の異なる実装方法を示しています。

Junior Go programmer

初心者のGoプログラマーが書くことが多い、繰り返し文を用いた階乗計算の実装です。forループを使って階乗を計算し、その結果を返します。

package fac

func Factorial(n int) int {
	res := 1

	for i := 1; i <= n; i++ {
		res *= i
	}

	return res
}

Functional Go programmer

機能型プログラミングスタイルを用いた階乗計算の実装です。再帰関数を使って階乗を計算しています。

package fac

func Factorial(n int) int {
	if n == 0 {
		return 1
	} else {
		return Factorial(n - 1) * n
	}
}

Generic Go programmer

汎用的なGoプログラマーが書く、型をインタフェースで指定した実装です。関数の引数と戻り値の型をinterface{}にして、具体的な型に依存しない実装にしています。

package fac

func Factorial(n interface{}) interface{} {
	v, valid := n.(int)
	if !valid {
		return 0
	}

	res := 1

	for i := 1; i <= v; i++ {
		res *= i
	}

	return res
}

Multithread optimized Go programmer

マルチスレッドを最適化した階乗計算の実装です。並列化されたゴルーチンを用いて、階乗の計算を2つの部分に分けて実行し、その結果を最後に掛け合わせることで全体の計算を高速化しています。sync.WaitGroupを使ってゴルーチンの完了を待ち、最後に結果を返します。

package fac

import "sync"

func Factorial(n int) int {
	var (
		left, right = 1, 1
		wg sync.WaitGroup
	)

	wg.Add(2)

	pivot := n / 2

	go func() {
		for i := 1; i < pivot; i++ {
			left *= i
	
	}

	wg.Done()
}()

go func() {
	for i := pivot; i <= n; i++ {
		right *= i
	}

	wg.Done()
}()

wg.Wait()

return left * right

Discovered Go patterns

階乗を計算し結果をチャネルに渡す実装です。チャネルを使って非同期処理が可能になります。この実装では、計算中の階乗の値をチャネルに送信し、チャネルから値を受信することで階乗を計算できます。

package fac

func Factorial(n int) <-chan int {
	ch := make(chan int)

	go func() {
		prev := 1

		for i := 1; i <= n; i++ {
			v := prev * i

			ch <- v

			prev = v
		}

		close(ch)
	}()

	return ch
}

Fix Go weaknesses with mature solutions

オブジェクト指向プログラミングスタイルを採用した階乗計算の実装です。インターフェースと構造体を用いて、階乗計算の処理をカプセル化しています。コード内では、メソッドとドキュメンテーションが豊富に記述されており、機能の説明や役割が明確になっています。

package fac

/**
 * @see https://en.wikipedia.org/wiki/Factorial
 */
type IFactorial interface {
	CalculateFactorial() int
}

// FactorialImpl implements IFactorial.
var _ IFactorial = (*FactorialImpl)(nil)

/**
 * Used to find factorial of the n.
 */
type FactorialImpl struct {
	/**
	 * The n.
	 */
	n int
}

/**
 * Constructor of the FactorialImpl.
 *
 * @param n the n.
 */
func NewFactorial(n int) *FactorialImpl {
	return &FactorialImpl{
		n: n,
	}
}

/**
 * Gets the n to use in factorial function.
 *
 * @return int.
 */
func (this *FactorialImpl) GetN() int {
	return this.n
}

/**
 * Sets the n to use in factorial function.
 *
 * @param n the n.
 * @return void.
 */
func (this *FactorialImpl) SetN(n int) {
	this.n = n
}

/**
 * Returns factorial of the n.
 *
 * @todo remove "if" statement. Maybe we should use a factory or somthing?
 *
 * @return int.
 */
func (this *FactorialImpl) CalculateFactorial() int {
	if this.n == 0 {
		return 1
	}

	n := this.n
	this.n = this.n - 1

	return this.CalculateFactorial() * n
}

Senior Go programmer

シニアGoプログラマーが書く、シンプルで効率的な階乗計算の実装です。forループを用いて階乗を計算し、その結果を返します。この実装は、Junior Go programmerと同じアプローチをとっていますが、コメントが簡潔に記述されており、コードが洗練されています。

package fac

// Factorial returns n!.
func Factorial(n int) int {
	res := 1

	for i := 1; i <= n; i++ {
		res *= i
	}

	return res
}

Rob Pike

Go言語の共同設計者であるRob Pikeが書くであろう、シンプルで効率的な階乗計算の実装です。これもSenior Go programmerのコードと同じアプローチをとっており、わずかなコメントで機能が説明されています。

package fac

// Factorial returns n!.
func Factorial(n int) int {
	res := 1

	for i := 1; i <= n; i++ {
		res *= i
	}

	return res
}

まとめ

Go言語で階乗を計算する方法はたくさんあり、それぞれのプログラマーのスキルや経験に応じて実装が変わります。Goプログラマーがさまざまなコーディングスタイルやアプローチで階乗計算を行う方法があり、それぞれの実装を理解することで、Go言語の特性や機能をより深く理解できるかと思いました。

109
76
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
109
76

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?