Hatena::Groupbioruby

"aac".translate #=> "N" このページをアンテナに追加 RSSフィード

2007-10-10情報技術基礎I

プログラミング基礎II くりかえし、式、条件分岐、正規表現

|  プログラミング基礎II くりかえし、式、条件分岐、正規表現 - "aac".translate #=> "N" を含むブックマーク はてなブックマーク -  プログラミング基礎II くりかえし、式、条件分岐、正規表現 - "aac".translate #=> "N"  プログラミング基礎II くりかえし、式、条件分岐、正規表現 - "aac".translate #=> "N" のブックマークコメント

この章ではくりかえし、式、条件分岐、正規表現を紹介します。これらはバイオインフォマティクスにかぎらずプログラミングでは非常に良く利用されるものです。特に正規表現DNA配列を文字列をして扱う現場では必須のものです。また、ここではプログラミングに特有の概念を数多く紹介します。


くりかえし

繰り返しは、プログラミング言語 Ruby ではとても簡単におこなうことができます。

100 回くりかえす。

▼繰り返し単位

puts "Hello !"

△実行結果

bioruby> puts "Hello !"
Hello !
  ==> nil

▼これを 100 回くり返すには、つぎのようにします。

100.times { puts "Hello !" }

△実行結果

bioruby> 100.times { puts "Hello !" }
Hello !
Hello !
Hello !
Hello !
Hello !
(中略)
Hello !
Hello !
Hello !
Hello !
Hello !
  ==> 100

{ と } で囲まれるプログラム部分(この例では { puts "Hello !" })はブロックとよばれます。100.times は後ろにブロックをとり、それを 100 回くりかえします。ブロックRuby ではとても頻繁に利用されます。

ここは Ruby の面白いところの一つです。興味のある方はブロックに着目して Ruby の本を読んでみると良いでしょう。


100 回くりかえす、番号付き

▼100 回くりかえす、番号付き

100.times {|i| 
  puts "Hello ! " + i.to_s 
}

△実行結果

bioruby> 100.times {|i| 
bioruby+   puts "Hello ! " + i.to_s 
}
Hello ! 0
Hello ! 1
Hello ! 2
Hello ! 3
Hello ! 4
(中略)
Hello ! 95
Hello ! 96
Hello ! 97
Hello ! 98
Hello ! 99
  ==> 100

100.times は繰り返し回数の番号を提供しています。 | と | の間の変数 i に繰り返し番号を繰り返しのたびに代入します。繰り返し回数は 0 からはじまります。

とにかくある回数くり返したいというときは times が便利です。


配列 Array の要素をひとつづつ表示する

集合の要素をひとつづつ評価したいというときは each をつかいます。

▼each を使います。

array = [0, 1, 2, 3, 4]
array.each {|x|
  puts x
}

△実行結果

bioruby> array = [0, 1, 2, 3, 4]
  ==> [0, 1, 2, 3, 4]
bioruby> array.each {|x|
bioruby+   puts x
}
0
1
2
3
4
  ==> [0, 1, 2, 3, 4]

このように、each をつかうと配列 Array の要素をひとつづつ評価することができます。

配列の要素をそれぞれ二乗して出力します。

array.each {|x|
  puts x ** 2
}

△実行結果

bioruby> array.each {|x|
bioruby+   puts x ** 2
}
0
1
4
9
16
  ==> [0, 1, 2, 3, 4]

これをふたたび配列に戻すこともできます。それには map を使います。

map を使います。

array.map {|x|
  x ** 2
}

△実行結果

bioruby> array.map {|x|
bioruby+   x ** 2
}
  ==> [0, 1, 4, 9, 16]

each のかわりに map をつかいます。map写像のことです。

map の結果を array2 に代入します。

array
array2 = array.map {|x|
  x ** 2
}
array2

△実行結果

bioruby> array
  ==> [0, 1, 2, 3, 4]
bioruby> array2 = array.map {|x|
bioruby+   x ** 2
}
  ==> [0, 1, 4, 9, 16]
bioruby> array2
  ==> [0, 1, 4, 9, 16]

このように map配列 Array のそれぞれの要素に同じ操作をおこなうことができ、非常に便利です。一連のデータを変換するということを抽象化しています。


添字付きの each

配列 Array の添字(index)を同時に使いたいときは each_with_index を使います。

array.each_with_index {|x, i|
  puts [i.to_s, x.to_s].join(" ")
}

△実行結果

bioruby> array.each_with_index {|x, i|
bioruby+   puts [i.to_s, x.to_s].join(" ")
}
0 0
1 1
2 2
3 3
4 4
  ==> [0, 1, 2, 3, 4]

ハッシュ Hash の要素をひとつづつ評価する

▼each を使います。

hash = {"DNA" => "ACGT", "RNA" => "ACGU"}
hash.each {|key, value|
  puts [key, value].join(" ")
}

△実行結果

bioruby> hash = {"DNA" => "ACGT", "RNA" => "ACGU"}
  ==> {"DNA"=>"ACGT", "RNA"=>"ACGU"}
bioruby> hash.each {|key, value|
bioruby+   puts [key, value].join(": ")
}
DNA: ACGT
RNA: ACGU
  ==> {"DNA"=>"ACGT", "RNA"=>"ACGU"}
くりかえしのまとめ
  1. くりかえしでは each が中心的な役割をになっています。
  2. 配列 Array でもハッシュ Hash のどちらでも each がつかえます。
  3. 集合の要素全体への操作(写像)は map でおこないます。

式とは、プログラム単位です。プログラムは式で構成されます。いままで実習でコピーペーストしてきたものはすべて式です。

基本的に、式は値を返します。いままで bioruby> プロンプトでコードを実行した結果、 ==> につづく表示は式の値です。

▼式 "ACGT"

"ACGT"

△実行結果

bioruby> "ACGT"
  ==> "ACGT"

式 "ACGT" の値は "ACGT" です。

▼式 dna = "ACGT"

dna = "ACGT"

△実行結果

bioruby> dna = "ACGT"
  ==> "ACGT"

▼式 puts dna

puts dna

△実行結果

bioruby> puts dna
ACGT
  ==> nil

puts dna の値は nil です。nil は特別な値で、空(カラ)を表現します。

式のまとめ
  1. 実は式をコピーペーストしてました。
  2. nil は空の値です。

条件分岐

条件分岐は、プログラムの中の処理を制御構造です。条件に関係する真偽判定と、分岐の制御構造について紹介します。


真偽判定

条件分岐には、真偽判定を利用します。式の値として真偽を取るものがあります。真には true、偽には false が値として利用します。

▼比較、大小

1 < 2

△実行結果

bioruby> 1 < 2
  ==> true

▼比較、大小

1 > 2

△実行結果

bioruby> 1 > 2
  ==> false

▼比較、大小、一致を含む

1 <= 1

△実行結果

bioruby> 1 <= 1
  ==> true

▼比較、大小、一致を含む

1 >= 2

△実行結果

bioruby> 1 >= 2
  ==> false

▼比較、一致、不一致

1 == 1

△実行結果

bioruby> 1 == 1
  ==> true

▼比較、一致、不一致

1 != 2

△実行結果

bioruby> 1 != 2
  ==> true

if

条件分岐は、if が基本です。同じ変数の内容で多数の分岐条件を扱うときは caseをつかいます。

▼条件分岐には、if を用います。次の例は、塩基配列の文字列を一文字づつ評価して、"A" のときに "Adenine." と出力するプログラムです。

dna = "ACGT"
dna.split("").each {|nucleotide|
  if nucleotide == "A" then
    puts "Adenine."
  end
}

△実行結果

bioruby> dna.split("").each {|nucleotide|
bioruby+   if nucleotide == "A" then
    puts "Adenine."
  end
}
Adenine.
  ==> ["A", "C", "G", "T"]

さて、このプログラムでは、nucleotide == "A" という条件節(if nucleotide == "A")を評価して、"A" なら "Adenine." を出力し、そうでないならなにもしない、という動作をします。

▼"A" のときの条件節を確認します。

nucleotide = "A"
nucleotide == "A"

△実行結果

bioruby> nucleotide = "A"
  ==> "A"
bioruby> nucleotide == "A"
  ==> true

一致判定 == で true が返っています。

▼"T" の場合の条件節を確認します。

nucleotide = "T"
nucleotide == "A"

△実行結果

bioruby> nucleotide = "T"
  ==> "T"
bioruby> nucleotide == "A"
  ==> false

一致判定 == で false が返っています。

このように条件節の真偽値で評価されて、条件分岐の if で利用されています。if では条件節が true の場合に then のつづき(ここでは、puts "Adenine." )を実行します。

if-else

条件分岐で規定(どの条件でも true でなかった場合)の動作を用意したいことがあります。そのときには、if-then-else をつかいます。else 以下が規定の動作になるので、すべての条件節で true にならないときは else 以下に分岐します。

▼"A" のときには puts "Adenine." を、それ以外には puts "Others." を実行します。

dna = "ACGT"
dna.split("").each {|nucleotide|
  if nucleotide == "A" then
    puts "Adenine."
  else
    puts "Others."
  end
}

△実行結果

bioruby> dna = "ACGT"
  ==> "ACGT"
bioruby> dna.split("").each {|nucleotide|
bioruby+   if nucleotide == "A" then
    puts "Adenine."
  else
bioruby+     puts "Others."
  end
}
Adenine.
Others.
Others.
Others.
  ==> ["A", "C", "G", "T"]

case-when

変数の値によって分岐するような場合は,条件の数が少ないときはよいのですが、if-then-else だと煩雑になりがちです。

▼そのような場合は、case-when で多数の条件を分岐します。

dna = "ACGT"
dna.split("").each { |nucleotide|
  case nucleotide
  when "A","G"
    puts "Purine."
  when "C","T","U"
    puts "Pyrimidine."
  end
}

△実行結果

bioruby> dna = "ACGT"
  ==> "ACGT "
bioruby> dna.split("").each { |nucleotide|
bioruby+   case nucleotide
  when "A","G"
    puts "Purine."
  when "C","T","U"
    puts "Pyrimidine."
  end
}
Purine.
Pyrimidine.
Purine.
Pyrimidine.
  ==> ["A", "C", "G", "T"]

case-when も if と同様に else を使うことができます。

when のあとにつづく文字列だけではありません。このあとに説明する正規表現も利用できます。それはのちほど説明します。


条件分岐のまとめ
  1. 真偽値は特別な値 true と false
  2. 真偽値は比較で返る
  3. ちょっと複雑な分岐は case-when で簡単になることがある。

正規表現

正規表現は文字列の操作において、とても重要なものです。正規表現は文字列の集合の表現方法です。たとえば、「アルファベットの小文字全部」は [a-z] で表現できます。

正規表現をつかうとできること
  1. 配列中のモチーフを検索できる。
  2. 文字列中から情報を抜き出す。

DNA配列大文字小文字)で長さ一文字以上
/[ACGTacgt]+/

マッチする例

"acgt" =~ /[ACGTacgt]+/

△実行結果

bioruby> "acgt" =~ /[ACGTacgt]+/
  ==> 0

マッチのある場合は、マッチした位置を返します。

[ACGTacgt] が、それらのなかの一文字を表現し、+ が一文字以上を表現しています。したがって、/[ACGTacgt]+/ は「ACGTacgtのなか文字が一文字以上つづく文字列」の正規表現です。正規表現は / と / で囲まれたものとして利用できます。

ここでは、文字種と長さが別の記号で表現されていることを押さえてください。

マッチしない例

"bbbb" =~ /[ACGTacgt]+/

△実行結果

bioruby> "bbbb" =~ /[ACGTacgt]+/
  ==> nil

マッチしない場合は、nil を返します。

文字種
  • 文字全体を表現する記号は、\w です。文字全体以外を表現するのは、\W です。
  • 数値全体を表現する記号は、\d です。数値以外全部を表現するのは、\D です。
  • それ以外の文字(スペース、タブ、改行など)を表現するの記号は、\s です。それ以外以外を表現するのは、\S です。

任意の一文字を表現するのは、. です。

長さ
  • 一文字以上は +
  • 零文字以上は *
  • 長さ指定は {長さ}
  • 長さ範囲指定は {最低長,最高長}
さらに位置情報の記号もあります。
  • 先頭 ^
  • 最後尾 $

▼標準遺伝コードでの終止コドンの正規表現

stop_codon = /U(A[AG]|GA)/
"UAA" =~ stop_codon
"UAG" =~ stop_codon
"UGA" =~ stop_codon
"AUG" =~ stop_codon

△実行結果

bioruby> top_codon = /U(A[AG]|GA)/
  ==> /U(A[AG]|GA)/
bioruby> "UAA" =~ stop_codon
  ==> 0
bioruby> "UAG" =~ stop_codon
  ==> 0
bioruby> "UGA" =~ stop_codon
  ==> 0
bioruby> "AUG" =~ stop_codon
  ==> nil

/U(A[AG]|GA)/ の解説をします。

  1. 終止コドンは "UAA", "UAG", "UGA" の三種類あります。
  2. 三種類の文字列のうちどれかという正規表現では /(UAA|UAG|UGA)/ になります。
    1. | が or を表現し、() でグルーピングされています。(文字列のグルーピング
  3. つぎに、終始コドンがすべて U からはじまっているので、それを /U(AA|AG|GA)/ と表現できることがわかります。
  4. コドンの二文字目が共通の終止コドン UAA と UAG もまとめると、/U(A[AG]|GA)/ になりました。(文字のグルーピング
    1. A[AG] は AA もしくは AG という意味になります。

このように、正規表現マッチする対象の文字列がおなじでも、表現方法がいくつもあります。より短い正規表現をつくることは職人芸的なものが必要になる場合もあります。

正規表現は、文字のマッチに利用します。文字のマッチを使う場合は、

  1. 文字列の置換(gsub, sub
  2. 文字列の検出(scan
  3. 真偽判定(if、case-when、=~)

があげられます。

たとえば、十分長い文字列がDNA配列RNA配列であるのかを判定するようなプログラムではつぎのようになります。

case sequence
when /^[ACGT]+$/
  puts "DNA"
when /^[ACGU]+$/
  puts "RNA"
end

DNA の場合

sequence = 'ACTG' * 100
case sequence
when /^[ACGT]+$/
  puts "DNA"
when /^[ACGU]+$/
  puts "RNA"
end

△実行結果

bioruby> sequence = 'ACTG' * 100
  ==> "ACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTGACTG"
bioruby> case sequence
when /^[ACGT]+$/
  puts "DNA"
when /^[ACGU]+$/
  puts "RNA"
end
DNA
  ==> nil

DNA に判定されました。


RNA の場合

sequence = 'AUCG' * 100
case sequence
when /^[ACGT]+$/
  puts "DNA"
when /^[ACGU]+$/
  puts "RNA"
end

△実行結果

bioruby> sequence = 'AUCG' * 100
  ==> "AUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCGAUCG"
bioruby> case sequence
when /^[ACGT]+$/
  puts "DNA"
when /^[ACGU]+$/
  puts "RNA"
end
RNA
  ==> nil

RNA に判定されました。


マッチした文字列の取得

マッチした文字列はマッチ変数 $1〜9 に格納されます。

if position = "acgtttttgagtggtaccgttttat" =~ /([acgt]gt)/
  p position
  p $1
end

△実行結果

bioruby> if position = "acgtttttgagtggtaccgttttat" =~ /([acgt]gt)/
  p position
  p $1
end
1
"cgt"
  ==> nil

/([acgt]gt)/ にマッチする文字列は、1番目の位置の文字(二文字目)からの cgt というのがわかりました。

でも、ほかにもマッチしそうな文字列があります。

▼複数のマッチする文字列をすべて取得するには、scan をつかいます。

matches = "acgtttttgagtggtaccgttttat".scan(/([acgt]gt)/)
p matches

△実行結果

bioruby> matches = "acgtttttgagtggtaccgttttat".scan(/([acgt]gt)/)
  ==> [["cgt"], ["agt"], ["ggt"], ["cgt"]]
bioruby> p matches
[["cgt"], ["agt"], ["ggt"], ["cgt"]]

scan をつかえば、終止コドンになりそうな部分の列挙が出来ることが想像できると思います。

正規表現のまとめ
  1. 正規表現は文字列の集合の表現方法。
  2. 文字種と長さと位置は別の記号になっている。
  3. マッチした位置と文字列を取得できる。

You-KeyYou-Key2007/11/17 07:36KNOB をマックOS Xに ダウンロードしたのですが、その後どのようにして使ったら良いのかわかりません。
Parallel virtual system も つかっています。基本的な質問ですみません。

You-KeyYou-Key2007/11/17 13:10すみません。解りました。KNOB2.0.0 を Download してknob-2.0.0.iso になった file をParallel を Double click して出てきたWindow からNew をselectして、さらにTypicalをselect, and Next, OS Type Linux, OS version, Other linux. そして、More Option で、iso file を指定すれば良いのでした。、、、 昨日も、Ruby on Rails の Bruce Tate さんにつまらない質問をして、FirstRule of Kayak: When in doubt, paddle like hell。と言う、email を頂いた所でした。

トラックバック - http://bioruby.g.hatena.ne.jp/nakao_mitsuteru/20071010