Go言語をとりあえず使ってみる

Go 1.9 で。使ってみると遊びなら C言語を使うくらいなら Go の方がいいという感じ。
Go 結構いいな。「見た目派」の自分としては、ソースの見た目がすっきりしているし。:= のおかげで型をあんまり明示的に書かなくても済むのが Rubyist にはいいっぽい。
それから $ go run がインタプリタみたいで超ラク。これはいい。

少しだけ触った感じでは、modify された C ? C言語はどうも面倒なのだけれど、これはちょっと使ってみたくなる。とにかくすっきりしている。

とりあえず FizzBuzz。switch 文の使い方がおもしろい。

package main
import "fmt"
import "strconv"

func get(i int) (st string) {
    switch 0 {
    case i % 15:
        st = "FizzBuzz"
    case i % 3:
        st = "Fizz"
    case i % 5:
        st = "Buzz"
    default:
        st = strconv.Itoa(i)
    }
    return
}

func main() {
    const num = 50
    ar := [num]string{}
    for i := 0; i < num; i++ {
        ar[i] = get(i + 1)
    }
    fmt.Println(ar)
}

結果。

[1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz 16 17 Fizz 19 Buzz
Fizz 22 23 Fizz Buzz 26 Fizz 28 29 FizzBuzz 31 32 Fizz 34 Buzz Fizz 37 38
Fizz Buzz 41 Fizz 43 44 FizzBuzz 46 47 Fizz 49 Buzz]

 
フィボナッチ数列

package main
import "fmt"

func main() {
    a, b := 1, 0
    result := make([]int, 0, 50)
    for a < 1000 {
        a, b = a + b, a
        result = append(result, b)
    }
    fmt.Println(result)
}
//=>[1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987]

 
バブルソート。「メソッド」を使ってみました。Arr 型(ユーザー定義型)を使わずに直接的に配列で []int では、メソッドは呼べません。[]int は名前のない型なので。

package main
import "fmt"

type Arr []int

func (arr Arr) Bubble_sort() Arr {
    ar := append(Arr{}, arr...)    //=>スライスを複製(参照先を変える)  Arr{} は空スライス。
    ln := len(ar)
    for i := 0 ; i < ln - 1; i++ {
        for j := 1; j < ln - i; j++ {
            if ar[j - 1] > ar[j] {
               ar[j], ar[j - 1] = ar[j - 1], ar[j]
            }
        }
    }
    return ar
}

func (ar Arr) Sum() int {
    sum := 0
    for _, i := range ar {
        sum += i
    }
    return sum
}

func main() {
    given := Arr{5, 2, 1, 8, 6, 3}
    fmt.Println(reflect.ValueOf(given).Kind())    //given の型
    fmt.Println(given.Bubble_sort())
    fmt.Println(given)
    fmt.Println(given.Bubble_sort().Sum())
}

//slice
//=>[1 2 3 5 6 8]
//=>[5 2 1 8 6 3]    (破壊されていない)
//=>25
//[...]int{1, 2, 3, 4} は配列、[]int{1, 2, 3, 4] はスライス

given はスライスで、スライスは参照型なので、メソッド先で given が破壊されないよう append で複製しています。(←わかりにくかった!)
バブルソートして sum() でスライスの和を求めています。意味ないけれど(笑)。Rubyist としてはメソッドチェーンができるかやってみました。
sum() 内では ar の値を for 文で取り出すとき、インデックスの値を捨てなくてはいけないことに気づかなくてハマりました。
配列は長さが固定なのだけれど、スライスは配列の中で長さが可変の配列っぽい処理ができるわけだ。なるほど合理的。


エラトステネスの篩。

package main
import "fmt"
import "math"

const max = 100
var field [max + 1]int

func sieve() int {
    for i := 2; i <= int(math.Sqrt(float64(max))); i++ {
        if field[i] != 0 {continue}
        for j := 2; j <= max / i; j++ {
            field[i * j] = 1
        }
    }
    i := 0
    for j := 2; j <= max; j++ {
        if field[j] == 0 {
            field[i] = j
            i++
        } 
    }
    return i
}

func main() {
    fmt.Println(field[:sieve()])
}
//=>[2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97]

1000万までの篩を実行してみたところ(出力はなし)、0.331秒でした。ちなみに Swift で 0.234秒、Ruby で 3.01秒、Ruby FFI(本体部分は C)で 0.459秒というところです。C と同じくらいですか。
field[i++] = j みたいなインクリメント演算子の使い方はできないのですね。

Go言語を Linux Mint(Ubuntu)にインストール

基本的には上のサイトどおりにやればよい。Linux Mint 18.3 で確認。
 
バージョン 1.9 を apt でインストールします。(追記:いまではバージョン 1.10 がインストールできるようです。10/16)

$ sudo add-apt-repository ppa:gophers/archive
$ sudo apt-get update
$ sudo apt-get install golang-1.9-go

 
あとは上サイトにも書いてあるが、Go は /usr/lib/go-1.9/bin にあるので、ここにパスを通す。ホームディレクトリで

$ cd ~/
$ echo 'export PATH=$PATH:/usr/lib/go-1.9/bin' >> .bash_profile 

あるいは自動ログインしている人は

$ echo 'export PATH=$PATH:/usr/lib/go-1.9/bin' >> .bashrc

きちんとインストールされてパスが通っていれば、再ログインで

$ go version
go version go1.9.4 linux/amd64

こうなれば OK。
 

$GOPATH

必ず $GOPATH を設定せねばなりません。これは Go言語のワークスペースであり、どこでもかまいません。例えば

$ echo 'export GOPATH=$HOME/Documents/Go' >> .bash_profile
$ echo 'export PATH=$PATH:$GOPATH/bin' >> .bash_profile

$GOPATH/bin にパスを通したのは、ここに Go で書かれたコマンドラインツールが配置されるからです。例えば

$ go get github.com/motemen/gore

とすると、gore という Go言語の REPL がインストールされます。./bin に gore コマンドが出来ているのがわかると思います。
 

Hello, World

hello_world.go

package main
import "fmt"

func main() {
    fmt.Println("Hello, World!")
} 

コンパイル後直ちに実行。(実行ファイルを作らない。)

$ go run hello_world.go
Hello, World!

コンパイルと実行。

$ go build hello_world.go
$ ./hello_world
Hello, World!

こんな感じです。


※追加(2018/2/11)
Geany で Go をサポートする。
http://go-lang.cat-v.org/text-editors/geany/
Tour Of Go。
A Tour of Go

Object#method の奇妙なること(Ruby)

Object#method はメソッドをオブジェクト化してくれるのですよね。だから、こんな奇妙なブロックなしの世界が…。

[1] pry(main)> putout = method(:p)
=> #<Method: Object(Kernel)#p>
[2] pry(main)> a = [1, 5, 4, -2, 10, 3]
=> [1, 5, 4, -2, 10, 3]
[3] pry(main)> a.each(&putout)
1
5
4
-2
10
3
=> [1, 5, 4, -2, 10, 3]
[4] pry(main)> a.each_cons(2, &putout)
[1, 5]
[5, 4]
[4, -2]
[-2, 10]
[10, 3]
=> nil

Kernel.#p もこうしてブロックなしで使えてしまう…。

Rubyトリビアでした。
 

追記

上はこの挙動が元になっています。

[5] pry(main)> putout.call("Tokyo")
"Tokyo"
=> "Tokyo"

例えば a.each(&putout) は、call() が自動的に補われて a.each {|x| putout.call(x)} と同等になります。

なお、よく使われるイデオムである例えば%w(a b c).map(&:upcase) というのは、to_proc.call() が自動的に補われて %w(a b c).map {|x| :upcase.to_proc.call(x)} と同等になります。よく似ていますね。さらにいうと、:upcase.to_proc.call(x) というのは、x.upcase と同等です。うーん、面倒だ。

Linux で tty(仮想コンソール)が黒い画面のままで起動しない

Linux Mint 18.3 Cinnamon 64 bit を使っていて端末エミュレータもふつうに使えるのですが、[Ctrl] + [Alt] + [F1] ~ [F6] で立ち上がる筈の tty(仮想コンソール)が、黒い画面のままでどうしようもありません。ここから 、[Ctrl] + [Alt] + [F8] で X Window System には戻ります。

PC は VAIO Fit 15E mk2 VJF1521、CPU は Core i5-4210U、グラフィックスカードは Inter Corporation Haswell-UTL Integrated Graphics Controller で、カーネルは 4.4.0-112-generic です。


ここのとおりやったら tty が使えるようになりました。

まず [Crtl] + [Alt] + [T] などで端末エミュレータを起動します。そして

$ sudo sed -i -e 's/#GRUB_TERMINAL/GRUB_TERMINAL/g' /etc/default/grub
$ sudo update-grub

を実行して下さい。その後再起動して、[Ctrl] + [Alt] + [F1] で tty が立ち上がれば OK です。

すべての組み合わせについて配列を n 個に分割する(Ruby)

Array#divide(n) です。遊びで作ってみました。
配列を n 個に分割する、すべての組み合わせをブロックに渡します。ブロックが与えられなければ Enumerator を返します。

[*2..6].divide(3) {|i| p i} 
#=>
#[[2], [3], [4, 5, 6]]
#[[2], [3, 4], [5, 6]]
#[[2], [3, 4, 5], [6]]
#[[2, 3], [4], [5, 6]]
#[[2, 3], [4, 5], [6]]
#[[2, 3, 4], [5], [6]]
p "Ruby".chars.divide(3).with_object([]) {|i, h| h << i.map(&:join)}
#=>[["R", "u", "by"], ["R", "ub", "y"], ["Ru", "b", "y"]]

あんまり使いみちは思いつかないのですが(笑)。
 
コード。
divide.rb

class Array
  def divide(n)
    if !n.class.ancestors.include?(Integer) or !n.between?(1, size)
      raise ArgumentError, "can not devide into #{n}."
    end
    e = Enumerator.new do |y|
      [*0...size - 1].combination(n - 1) do |ar|
        ar.sort!
        take_num = []
        ([-1] + ar + [size - 1]).each_cons(2) {|i, j| take_num << j - i}
        result = []
        tmp = self
        take_num.each do |i|
          result << tmp.take(i)
          tmp = tmp.drop(i)
        end
        y << result
      end
    end
    block_given? ? loop {yield(e.next)} : e
  end
end

Gem 'oekaki' にタートルグラフィックスを追加

前回のエントリで作った Turtle クラスを Gem 'oekaki' に同梱しました。これでバージョンは 0.1.5 になります。
oekaki | RubyGems.org | your community gem host
GTK+でお絵かきしてみた(Ruby) - Camera Obscura
 
使い方はほぼ同じです。インスタンスの生成に

t = Turtle.new(width, height, self)

だったのが、

t = Oekaki::Turtle.new

になるだけです。引数はまったく要りません。

例。

require 'oekaki'

Oekaki.app width: 600, height: 400, title: "C curve" do
  draw do
    clear
    
    t = Oekaki::Turtle.new
    t.move(-130, -100)
    t.color(0, 65535, 0)
    ratio = sqrt(2) / 2
    
    drawing = lambda do |length, depth|
      if depth.zero?
        t.forward(length)
      else
        t.left(45)
        drawing[length * ratio, depth - 1]
        t.right(90)
        drawing[length * ratio, depth - 1]
        t.left(45)
      end
    end
    
    drawing[260.0, 10]
  end
end

 

obelisk.hatenablog.com

タートルグラフィックスで再帰曲線をいろいろ描いてみる(Ruby)

Ruby で簡単なタートルグラフィックスを実装して、いろいろ再帰曲線を描いてみました。描画には自作の Gem 'oekaki' を使っています。
oekaki | RubyGems.org | your community gem host
GTK+でお絵かきしてみた(Ruby) - Camera Obscura
 
※追記
Gem化しました。
Gem 'oekaki' にタートルグラフィックスを追加 - Camera Obscura
再帰曲線を描くなら、L-system も参考になると思います。これも実装しました。
再帰曲線を描く言語「L-system」を Ruby で実装した - Camera Obscura
 

タートルグラフィックスの実装はこんな感じです。
turtle.rb(Gist

class Turtle
  def initialize(width, height, ob)
    @width, @height = width, height
    @pen_po = Vector[0, 0]
    @pen = ob
    @dir = Vector[1, 0]
    @color = [65535, 65535, 65535]
  end
  attr_accessor :pen_po, :dir
  
  def left(deg)
    θ = PI * deg / 180
    @dir = Matrix[[cos(θ), -sin(θ)], [sin(θ), cos(θ)]] * @dir
  end
  
  def right(deg)
    left(-deg)
  end
  
  def forward(length, draw = true)
    next_po = @pen_po + @dir * length
    if draw
      @pen.color(*@color)
      @pen.line(@width / 2 + next_po[0], @height / 2 - next_po[1],
         @width / 2 + @pen_po[0], @height / 2 - @pen_po[1])
    end
    @pen_po = next_po
  end
  
  def back(length)
    forward(-length, false)
  end
  
  def color(r, g, b)
    @color = [r, g, b]
    @pen.color(*@color)
  end
  
  def circle(radius, fill = false)
    @pen.color(*@color)
    @pen.circle(fill, @width / 2 + @pen_po[0], @height / 2 - @pen_po[1], radius)
  end
  
  def move(x, y)
    @pen_po = Vector[x, y]
  end
end

特にむずかしいところはないと思います。なお、内部の座標 @pen_po は原点がキャンバスの中心で、(x, y) 座標の向きは数学の慣例どおり(つまり、y 座標は上がプラス方向)です。メソッド left(), right() は向きの回転、forward(length, draw) は長さ length だけ前進(draw が false なら位置が移動するだけで描画されない)、circle(radius, fill) はペンの位置に半径 radius の円を描く(fill が true ならば内部を満たす)、move() はその座標へ移動、back(length) は length だけ戻る(描画はされない)。

Gem 'oekaki' が内部で require 'matrix', include Math をしているので、行列・ベクトル演算と Math モジュールがそのまま使えます。Turtle.new(w, h, self) は draw メソッドなどのブロック内で行って下さい。(インスタンスはブロックの外へ持ちだしてもかまいません。)


C曲線。Gist

require 'oekaki'
require_relative 'turtle'

Width, Height = 600, 400

Oekaki.app width: Width, height: Height, title: "C curve" do
  draw do
    clear
    
    t = Turtle.new(Width, Height, self)
    t.move(-130, -100)
    t.color(0, 65535, 0)
    ratio = sqrt(2) / 2
    
    drawing = lambda do |length, depth|
      if depth.zero?
        t.forward(length)
      else
        t.left(45)
        drawing[length * ratio, depth - 1]
        t.right(90)
        drawing[length * ratio, depth - 1]
        t.left(45)
      end
    end
    
    drawing[260.0, 10]
  end
end

 
シェルピンスキー曲線。Gist

require 'oekaki'
require_relative 'turtle'

Width, Height = 600, 600

Oekaki.app width: Width, height: Height, title: "Sierpinski curve" do
  draw do
    clear
    
    depth = 5
    l = 550
    num = 2 ** (depth - 1)
    
    t = Turtle.new(Width, Height, self)
    t.color(0xffff, 0, 0xffff)
    step = l / ((2 * sqrt(2) + 1) * num + num - 1)
    t.move(-l / 2 + step / sqrt(2), l / 2)
    
    drawing = lambda do |depth, angle = 45|
      if depth.zero?
        t.forward(step)
      else
        t.right(angle)
        drawing[depth - 1, -angle]
        t.left(angle)
        t.forward(step)
        t.left(angle)
        drawing[depth - 1, -angle]
        t.right(angle)
      end
    end
    
    4.times do
      drawing[2 * depth - 1]
      t.right(45)
      t.forward(step)
      t.right(45)
    end
  end
end

 
コッホ曲線。Gist

require 'oekaki'
require_relative 'turtle'

Width, Height = 600, 600

Oekaki.app width: Width, height: Height, title: "Koch curve" do
  draw do
    clear
    
    l = 500
    t = Turtle.new(Width, Height, self)
    t.color(0, 65535, 65535)
    t.move(l / 2, -150)
    t.dir = Vector[-1, 0]
    
    drawing = lambda do |length, depth|
      if depth.zero?
        t.forward(length)
      else
        drawing[length / 3, depth - 1]
        t.left(60)
        drawing[length / 3, depth - 1]
        t.right(120)
        drawing[length / 3, depth - 1]
        t.left(60)
        drawing[length / 3, depth - 1]
      end
    end
    
    3.times do
      drawing[l, 3]
      t.right(120)
    end
  end
end

 
ヒルベルト曲線。Gist

require 'oekaki'
require_relative 'turtle'

Width, Height = 600, 600

Oekaki.app width: Width, height: Height, title: "Hilbert curve" do
  draw do
    clear
    
    depth = 5
    
    l = 500
    t = Turtle.new(Width, Height, self)
    t.color(0xffff, 0x7dff, 0)
    t.move(-l / 2, l / 2)
    step = l / (2 ** depth - 1)
    
    drawing = lambda do |depth, angle|
      if depth.zero?
        return
      else
        t.right(angle)
        drawing[depth - 1, -angle]
        t.forward(step)
        t.left(angle)
        drawing[depth - 1,  angle]
        t.forward(step)
        drawing[depth - 1,  angle]
        t.left(angle)
        t.forward(step)
        drawing[depth - 1, -angle]
        t.right(angle)
      end
    end
    
    drawing[depth, 90]
  end
end

 
ドラゴン曲線。Gist

require 'oekaki'
require_relative 'turtle'

Width, Height = 600, 450

Oekaki.app width: Width, height: Height, title: "Dragon curve" do
  draw do
    clear
    
    depth = 12
    
    l = 350
    t = Turtle.new(Width, Height, self)
    t.color(0xf0ff, 0xffff, 0xffff)
    t.move(-l / 2, 50)
    
    drawing = lambda do |length, depth|
      if depth.zero?
        t.forward(length)
      else
        len = length / sqrt(2)
        po1 = t.pen_po
        t.forward(length, false)
        po2, t.pen_po = t.pen_po, po1
        t.right(45)
        drawing[len, depth - 1]
        t.pen_po = po2
        t.right(90)
        drawing[len, depth - 1]
        t.pen_po = po2
        t.left(135)
      end
    end
    
    drawing[l, depth]
  end
end

 

参考にしたサイト。(ありがとうございます!)
再帰プログラムによるフラクタル図形の描画:CodeZine(コードジン)
再帰曲線で遊ぼう
Pythonとタートルグラフィックスによる(再帰)プログラミング教育処方案 - Read -> Blog
特にいちばん下のブログ記事がすごいです。せっかくなので出来るだけ自力でやりました^^;

 

追加

このサイトの描画例を Python から移植しました。ありがとうございます!

格子曲線。Gist

require 'oekaki'
require_relative 'turtle'

Width, Height = 400, 400

Oekaki.app width: Width, height: Height, title: "Grate curve" do
  draw do
    clear(color(0xffff, 0x7dff, 0))
    
    t = Turtle.new(Width, Height, self)
    t.color(0, 0, 0)
    t.move(-160, 150)
    
    two = lambda do |a, c, w|
      return if c <= 1
      t.right(a)
      t.forward(1)
      t.right(a)
      t.forward(w)
      t.left(a)
      t.forward(1) if c > 1
      t.left(a)
      t.forward(w)
      two[a, c - 2, w]
    end
    
    square = lambda do |a, h, w|
      t.forward(w)
      two[a, h - 1, w]
    end
    
    grate = lambda do |n, a, w, h|
      if n.zero?
        square[a, h, w]
      else
        t.right(a)
        grate[n - 1, -a, h / 4, w]
        t.forward(h / 8)
        grate[n - 1,  a, h / 4, w]
        t.forward(h / 8)
        grate[n - 1, -a, h / 4, w]
        t.left(a)
      end
    end
    
    grate[4, 90, 320, 320]
  end
end

 
クヌース曲線。Gist

require 'oekaki'
require_relative 'turtle'

Width, Height = 600, 350

Oekaki.app width: Width, height: Height, title: "Knuth curve" do
  draw do
    clear
    
    t = Turtle.new(Width, Height, self)
    t.color(0xf0ff, 0xffff, 0xffff)
    t.move(250, -100)
    t.left(180)
    step = 8
    
    drawing = lambda do |depth, a, tn|
      if depth.zero?
        t.right(45 + tn)
        t.forward(step)
        t.left(45 + tn)
      else
        t.right(2 * tn + a)
        drawing[depth - 1, 2 * tn, -tn]
        t.right(45 - 3 * tn - a)
        t.forward(step)
        t.left(45 - tn + a)
        drawing[depth - 1, 0, -tn]
        t.right(a)
      end
    end
    
    drawing[9, -90, 45]
  end
end

 
まだコードの意味はよくわかっていないのですが(汗)。よく考えてみたいと思います。