コメント

コメントは /* と */ で囲む。
また、ANSI 標準ではないけど一行コメント // も使えるコンパイラも多いようだ。gcc で使えるか試してみよう。

#include

int main(void)
{
    printf("alpha\n");
    /* printf("bravo\n"); */
    printf("charlie\n");
    // printf("delta\n");
    printf("echo\n");
}

実行結果:

takatoh@nightschool $ ./sample_1_6
alpha
charlie
echo

お、/* */ だけじゃなくて一行コメントも使えてるな。

算術演算子

+ – * / % の5つがある。* / % は + – よりも優先度が高い。
% は剰余を計算するものなので整数型にしか使えない。

#include

int main(void)
{
    printf("%d\n", 2 + 3);
    printf("%d\n", 2 - 3);
    printf("%f\n", 2.0 * 3.0);
    printf("%f\n", 5.0 / 2.0);
    printf("%d\n", 5 / 2);
    printf("%d\n", 5 % 2);

    return 0;
}

実行結果:

takatoh@nightschool $ gcc sample_1_5.c -o sample_1_5
takatoh@nightschool $ ./sample_1_5
5
-1
6.000000
2.500000
2
1

整数の除算の余りは切り捨てられるってことでいいのかな。

プログラムに数値を入力する

scanf 関数でキーボードからの入力を受け取ることができる。一般的な書式は次の通り。

scanf("%d", &int型変数名);

浮動小数点数を受け取るには、1つ目の引数を “%f” にする。変数名の前に付いているアンパサンド(&)はポインタと関係してるはずだけどとりあえずはスルー。

サンプルプログラム。

#include

int main(void)
{
    int n;
    float f;

    printf("Input integer: ");
    scanf("%d", &n);

    printf("Input float: ");
    scanf("%f", &f);

    printf("%d\n", n);
    printf("%f\n", f);

    return 0;
}
takatoh@nightschool $ gcc sample_1_4.c -o sample_1_4
takatoh@nightschool $ ./sample_1_4
Input integer: 3
Input float: 2.5
3
2.500000

変数の宣言と値の代入

C は静的な型付け言語で、変数を利用するには予め宣言しておかないといけない。変数の宣言はこうする。

型 変数名, 変数名;

同じ型ならカンマで区切って複数の変数を宣言できる。
変数のスコープは、その宣言する場所によって決まる。関数の中で宣言した変数は関数の中だけで有効なローカル変数、関数の外で宣言すればグローバル変数になる。

C には 5つの基本的な型がある。

キーワード
文字char
符号付き整数int
浮動小数点数float
倍精度浮動小数点数double
値なしvoid

変数への値の代入は = を使う。ココらへんは他の言語と一緒。

じゃ、サンプルプログラムを書いてみよう。

#include

int main(void)
{
    int num;

    num = 100;
    printf("Value is %d\n", num);

    return 0;
}

実行結果:

takatoh@nightschool $ gcc sample_1_3.c -o sample_1_3
takatoh@nightschool $ ./sample_1_3
Value is 100

C言語はじめました

このあいだ Qt について書いたせいか、C/C++ を勉強してみたくなった。Qt は C++ が標準なんだけど、まずは C からはじめることにした。で、書庫から昔買った本を引っ張り出してきた。

[amazonjs asin=”4798115770″ local=”JP”]

ちょっと古い本だけどまあいいだろう。

早速、短いプログラムが出てきたので書いてみよう(本とはちょっと変えてある)。ファイルの拡張子は .c だ。

#include

int main(vold)
{
    printf("This is short C program.");
    return 0;
}

#include はヘッダファイルを読み込むための、プリプロセッサのディレクティブで、ここでは stdio.h というヘッダファイルを読み込むことをコンパイラに指示している。stdio.h には標準的な関数に関する情報が書かれている。この例では printf 関数を使うために読み込んでいる。

int main(void) は main 関数の定義の最初の部分。main が関数名で、() の中が引数のリスト。void というのは引数をとらないことを示している。int は関数の戻り値の型を指定している。
続く中括弧 {} の中が関数の本体。2つの文があって、printf 関数の呼び出しと return で戻り値 0 を指定している。文はセミコロンで終わらないといけない。

さて、じゃあこれをコンパイルしてみよう。

takatoh@nightschool $ gcc sample_1_1.c
takatoh@nightschool $ ls
a.out  sample_1_1.c

a.out というファイルが新しく出来た。これがコンパイルした結果の実行ファイルだ。実行ファイルの名前を指定するには -o オプションを使う。省略すると a.out という名前になるらしい。

takatoh@nightschool $ gcc sample_1_1.c -o sample_1_1
takatoh@nightschool $ ls
a.out  sample_1_1  sample_1_1.c

今度は sample_1_1 ファイルが出来た。これを実行してみよう。

takatoh@nightschool $ ./sample_1_1
This is short C program.takatoh@nightschool $

printf が自動では改行してくれないせいで、プロンプトが続けて表示されてしまっているけど、うまく動いてるようだ。

サンプルをもうひとつ。

#include

int main(vold)
{
    printf("This is ");
    printf("another ");
    printf("C program.\n");
    return 0;
}

文が増えた。基本的に、文は上から順に実行される。だからこのプログラムは “This is another C program.” と出力するはずだ。ついでに最後には改行を入れてみた。

takatoh@nightschool $ gcc sample_1_2.c -o sample_1_2
takatoh@nightschool $ ls
a.out  sample_1_1  sample_1_1.c  sample_1_2  sample_1_2.c
takatoh@nightschool $ ./sample_1_2
This is another C program.

うん、うまくいった。

二分木を使ったソート

ヒープじゃなくて単純な二分木を使ったソート。すなわち、

  • 左の子は親よりも小さい
  • 右の子は親よりも大きい

という構造。で、これを通りがけ順(左の枝、根、右の枝の順に調べる)に出力すると昇順にソートされる。

class BinaryTree

  def initialize(x)
    @val = x
    @left = nil
    @right = nil
  end

  def insert(x)
    if x < @val
      @left.nil? ? @left = BinaryTree.new(x) : @left.insert(x)
    else
      @right.nil? ? @right = BinaryTree.new(x) : @right.insert(x)
    end
  end

  def traverse(&block)
    @left.traverse(&block) unless @left.nil?
    block.call(@val)
    @right.traverse(&block) unless @right.nil?
  end

end

def sort(ary)
  btree = BinaryTree.new(ary.shift)
  ary.each{|x| btree.insert(x) }
  [].tap{|a| btree.traverse{|v| a << v } }
end

if $0 == __FILE__
  sample = (1..100).to_a.sample(10)
  puts "unsorted: " + sample.inspect
  result = sort(sample)
  puts "sorted:   " + result.inspect
end
takatoh@nightschool $ ruby btree_sort.rb
unsorted: [47, 97, 34, 39, 74, 44, 38, 5, 10, 53]
sorted:   [5, 10, 34, 38, 39, 44, 47, 53, 74, 97]

配列を使ったヒープソート

一昨日は二分木を使ったヒープソートを作った。配列を使ったヒープの構造についてはここのページのヒープの項が分かりやすい。

 cf. 二分木 (binary tree) とヒープ (heap) – Algorithms with Python

二分木のルートを番号 0 として、各ノードに幅優先で左から番号をふっていく。ふった番号を配列の添字だと考えれば、次の関係が成り立つ。
すなわち、親の添字を k とすると:

  • 左側の子の添字は 2 * k + 1
  • 右側の子の添字は 2 * k + 2

子の添字を k とすると:

  • 親の添字は floor((k – 1) / 2)

となる。
これで二分木を配列に置き換えられる。後はやることは基本的に一緒だ。

class Heap

  def initialize
    @heap = []
  end

  def insert(x)
    @heap << x
    upheap
    self
  end

  def shift
    v = @heap.shift
    @heap.unshift(@heap.pop)
    downheap
    v
  end

  def upheap
    k = @heap.size - 1
    j = (k - 1) / 2    # parent of k
    until k == 0 || @heap[j] < @heap[k]
      @heap[j], @heap[k] = @heap[k], @heap[j]
      k = j
      j = (k - 1) / 2
    end
  end

  def downheap
    k = 0
    i = 2 * k + 1    # left child of k
    j = 2 * k + 2    # right child of k
    until @heap[i].nil?    # no children
      if @heap[j].nil? || @heap[i] < @heap[j]
        if @heap[k] < @heap[i]
          break
        else
          @heap[i], @heap[k] = @heap[k], @heap[i]
          k = i
          i = 2 * k + 1
          j = 2 * k + 2
        end
      else
        if @heap[k] < @heap[j]
          break
        else
          @heap[j], @heap[k] = @heap[k], @heap[j]
          k = j
          i = 2 * k + 1
          j = 2 * k + 2
        end
      end
    end
  end

end   # of class Heap

def heap_sort(ary)
  heap = Heap.new
  ary.each{|x| heap.insert(x) }
  result = []
  while y = heap.shift
    result << y
  end
  result
end

if $0 == __FILE__
  ary = (1..100).to_a.sample(10)
  puts "unsorted: " + ary.inspect
  result = heap_sort(ary)
  puts "sorted: " + result.inspect
end

実行例:

unsorted: [15, 70, 93, 27, 5, 8, 26, 40, 74, 67]
sorted:   [5, 8, 15, 26, 27, 40, 67, 70, 74, 93]

[おまけ]

二分木を使ったヒープソート(heap_sort.rb)と今日の配列を使ったヒープソート(heap_sort2.rb)の実行時間の比較をしてみた。

takatoh@nightschool $ time ruby heap_sort.rb
unsorted: [85, 61, 68, 9, 73, 79, 26, 84, 21, 13]
sorted:   [9, 13, 21, 26, 61, 68, 73, 79, 84, 85]

real	0m0.102s
user	0m0.019s
sys	0m0.008s
takatoh@nightschool $ time ruby heap_sort2.rb
unsorted: [65, 26, 92, 51, 33, 31, 61, 97, 34, 57]
sorted:   [26, 31, 33, 34, 51, 57, 61, 65, 92, 97]

real	0m0.034s
user	0m0.017s
sys	0m0.017s

配列を使ったヒープソートのほうが3倍位速い。ま、そうだろうな…と思ったら、user の時間は大して変わらないな。

RubyからQtを使ってみる

Web を眺めてたら、こんなページを見つけたので試してみることにした。

 cf. QtをRubyで扱おう(qtbindings) – Laboratory of Scarlet

Qt はクロスプラットフォームの GUI フレームワーク。Ruby からは qtbindings (Windows では qtruby4)を経由して使える。
早速インストール。まずは関連ライブラリから。

takatoh@nightschool $ sudo apt-get install libqt4-gui libqt4-dev cmake

そして qtibindings のインストール。

takatoh@nightschool $ gem install qtbindings

さあ、これで使えるようになったはずだ。
上のリンク先に載っているサンプルプログラムを作ってみよう。

require 'Qt'

app = Qt::Application.new(ARGV)
hello = Qt::PushButton.new("Hello world!", nil)
hello.resize(300, 50)
hello.show

app.exec

実行結果

qt_hello

出来たみたい。

二分ヒープ(ヒープ木)を使ったヒープソート

ヒープソートって配列を使ってやるのがフツウみたいだけど、アルゴリズムのアイデアに忠実に二分ヒープ(木)を使ってやってみた。
参考にしたページはココらへん。

 cf. 二分木 (binary tree) とヒープ (heap) – Algorithms with Python
 cf. 二分ヒープ – ウィキペディア

class Node

  attr_accessor :val, :left, :right
  attr_reader :parent

  def initialize(val, parent = nil)
    @val = val
    @parent = parent
    @left = nil
    @right = nil
  end

end   # of class Node

class HeapTree

  def initialize
    @root = nil
  end

  def insert(val)
    if @root.nil?
      @root = Node.new(val)
    else
      v = vacant
      n = Node.new(val, v)
      if v.left.nil?
        v.left = n
      else
        v.right = n
      end
      upheap(n)
    end
  end

  def shift
    if @root.nil?
      nil
    else
      val = @root.val
      @root.val = delete_last
      downheap(@root)
      val
    end
  end

  def upheap(node)
    until node.parent.nil? || node.val > node.parent.val
      v = node.val
      node.val = node.parent.val
      node.parent.val = v
      node = node.parent
    end
  end

  def downheap(node)
    return nil if node.nil?
    until node.left.nil?
      if node.right.nil? || node.left.val < node.right.val
        if node.val > node.left.val
          v = node.val
          node.val = node.left.val
          node.left.val = v
          node = node.left
        else
          break
        end
      else
        if node.val > node.right.val
          v = node.val
          node.val = node.right.val
          node.right.val = v
          node = node.right
        else
          break
        end
      end
    end
  end

  def vacant
    q = [@root]
    while n = q.shift
      if n.left.nil? || n.right.nil?
        return n
      else
        q << n.left
        q << n.right
      end
    end
  end

  def last
    q = [@root]
    begin
      n = q.shift
      q << n.left if n.left
      q << n.right if n.right
    end until q.empty?
    n
  end

  def delete_last
    node = last
    if node.parent.nil?
      @root = nil
    else
      if node.parent.right == node
        node.parent.right = nil
      else
        node.parent.left = nil
      end
    end
    node.val
  end

end   # of HeapTree


if $0 == __FILE__
  ary = (1..100).to_a.sample(10)
  puts "unsorted: " + ary.inspect
  heap = HeapTree.new
  ary.each do |x|
    heap.insert(x)
  end
  sorted = []
  while v = heap.shift do
    sorted << v
  end
  puts "sorted: " + sorted.inspect
end

なんだかあんまりきれいじゃないけど。特に downheap のあたりが。 とりあえず出来たって感じ。 最初は upheap や downheap でノード自体を入れ替えようとしてたのでもっとひどかった。ノードはそのままにしておいて持っている値だけ入れ替えればいいことに気づいていくらかマシになった。 実行結果:

takatoh@nightschool $ ruby heap_sort.rb
unsorted: [75, 63, 96, 9, 21, 92, 48, 83, 51, 31]
sorted:   [9, 21, 31, 48, 51, 63, 75, 83, 92, 96]

文字列をリストで与えられた長さに区切る

例えば Ruby で書くとこういうやつ。

def split_with(str, lis)
  a = Array.new(lis.size, "A")
  tmpl = a.zip(lis).map{|x, y| x + y.to_s }.join("")
  str.unpack(tmpl)
end

p split_with("abcdefghij", [1, 2, 3, 4])
takatoh@nightschool $ ruby split_with.rb
["a", "bc", "def", "ghij"]

Scheme ではどうだろう。

フツウの再帰版

(define split-with
  (lambda (str lis)
    (let loop ((sl (string->list str)) (l lis))
      (cond
        ((null? l) '())
        (else (cons (list-&gt;string (take sl (car l)))
          (loop (drop sl (car l)) (cdr l))))))))

(print (split-with "abcdefghij" '(1 2 3 4)))

なんの工夫もない。

takatoh@nightschool $ gosh split-with.scm
(a bc def ghij)

末尾再帰版

ちょっと工夫して末尾再帰。

(define split-with
  (lambda (str lis)
    (let loop ((sl (string-&gt;list str)) (l1 lis) (l2 '()))
      (cond
        ((null? l1) (reverse l2))
        (else (loop (drop sl (car l1))
                (cdr l1)
                (cons (list-&gt;string (take sl (car l1))) l2)))))))

(print (split-with "abcdefghij" '(1 2 3 4)))
takatoh@nightschool $ gosh split-with2.scm
(a bc def ghij)

map-accum版

もう少し何かないかと探したら、map-accum があった。

(use gauche.collection)

(define split-with
  (lambda (str lis)
    (let ((f (lambda (n sl) (values (take sl n) (drop sl n)))))
      (receive (l s) (map-accum f (string-&gt;list str) lis)
      (map list->string l)))))

(print (split-with "abcdefghij" '(1 2 3 4)))

これはちょっと説明をしておく。split-with の中で let を使って手続き f を定義している。これは、リストの各要素に適用される手続きで、2つの引数をとって 2つの値を返す。多値ってやつだな。覚えてるぞ。values で2つの値を返せばいい。1つ目は map の様に集められて、もう1つは次の適用の引数になる。で、map-accum 自体も2つの値を返す。1つ目は f の1つ目の返り値を集めたリストで、もう1つは最後の適用の2つ目の返り値だ。値が2つ返ってくるので、receive を使って l と s で受けている。s の方は使ってないけどね。最後に、l は文字のリストのリストになているので、文字列のリストに変換して完了。

takatoh@nightschool $ gosh split-with3.scm
(a bc def ghij)

実行時間の比較

せっかくなので比較してみた。

takatoh@nightschool $ time gosh split-with.scm
(a bc def ghij)

real	0m0.012s
user	0m0.011s
sys	0m0.000s
takatoh@nightschool $ time gosh split-with2.scm
(a bc def ghij)

real	0m0.015s
user	0m0.009s
sys	0m0.005s
takatoh@nightschool $ time gosh split-with3.scm
(a bc def ghij)

real	0m0.023s
user	0m0.023s
sys	0m0.000s

再帰版と末尾再帰版はあんまり変わらないけど、map-accum 版は明らかに遅い。そういうもんか。