LoginSignup
0
0

More than 5 years have passed since last update.

Perl 6でNLP 5本ノック 『第1章: 準備運動』 05 ~ 09

Last updated at Posted at 2016-12-02

こんにちは、Perl 6アドベントカレンダーの三日目の投稿になります。

NLP 5本ノックということで、東北大学の言語処理100本ノックの『第1章: 準備運動』の 05~09までの5本をPerl 6で解きつつ解説をしていきたいと思います。

問題文は掲載しませんので、ブラウザの別窓で下記ページを参照しながらお楽しみください:
http://www.cl.ecei.tohoku.ac.jp/nlp100/#ch1

読み進める前に

注意!

  • 読者レベルとしては、Perl 6アドベントカレンダー1日目で紹介したPerl 6 introductionなどのチュートリアルを一通り終えたレベルを想定しています。
  • わかりやすく紹介するために、正確ではない表現がところどころ出てくるかもしれません。
  • 日本語訳が定着していないようなPerl 6独特の英単語を目にするかもしれません。基本的に解説中ではそのまま英単語で書き、その後補足しますのでご容赦ください。
  • Perl 6のモットーはTMTOWTDI (やり方は一つじゃない) です。特に正解はありません。でも、「これのほうがもっとかっこよく簡潔に書けるよ!」といった指摘は大歓迎です!

05. n-gram

コード

05.p6
char-ngram("I am an NLPer", 2).perl.say;
word-ngram("I am an NLPer", 2).perl.say;

my sub char-ngram(Str $text, Int $n) {
    my @chars = $text.comb;
    gather for ^@chars X $n -> ($start, $len) {
        next if $start + $len > @chars;
        take @chars[$start..^($start + $len)]
    }
}

my sub word-ngram(Str $text, Int $n) {
    my @words = $text.words;
    gather for ^@words X $n -> ($start, $len) {
        next if $start + $len > @words;
        take @words[$start..^($start + $len)]
    }
}

解説

sub char-ngram(Str $text, Int $n)

  1. $text.comb で引数の文字列を文字単位に分割し、リストを@charsに代入します
  2. gather for take イディオムでn-gramの文字列を取り出して遅延リストを生成します
    1. ^@words X $n で開始位置,文字列長の組のすべての組み合わせのリストを生成します
    2. 開始位置 + 文字列長が @wordsの長さを超える場合は、その開始位置,文字列長の組に対する処理を止めます
    3. @words中の[開始位置,開始位置 + 文字列長)`の箇所の文字列を取得します

sub word-ngram(Str $text, Int $n)

sub char-ngram(Str $text, Int $n) における$text.comb$text.wordsに置き換わっただけです。$text.wordsは引数の文字列を空白区切りで分割します

06. 集合

コード

06.p6
my Set \X = char-ngram("paraparaparadise", 2).Set;
my Set \Y = char-ngram("paragraph", 2).Set;

say X  Y;
say X  Y;
say X  Y;
say Y  X;

my sub char-ngram(Str $text, Int $n) {
    my @chars = $text.comb;
    gather for ^@chars X $n -> ($start, $len) {
        next if $start + $len > @chars;
        take @chars[$start..^($start + $len)]
    }
}

解説

NOTE: sub char-ngram05. n-gram で用いたものと同一です

  1. "paraparaparadise" のbi-gramのリストを生成し、.SetでSet型に変換し\X に代入します
  2. "paragraph" のbi-gramのリストを生成し、.SetでSet型に変換し\Y に代入します
  3. X ∪ YXY の和集合を求め、sayで出力します
  4. X ∩ YXY の積集合を求め、sayで出力します
  5. X ∖ Y で 集合 X - Y を求め、sayで出力します
  6. Y ∖ X で 集合 Y - X を求め、sayで出力します

補足

07. テンプレートによる文生成

コード

07.p6
from-template(12,"気温",22.4).say;

my sub from-template(Int $x, Str $y, Rat $z --> Str) {
    ($x, "時の", $y, "", $z).join
}

解説

sub from-template(Int $x, Str $y, Rat $z --> Str)

NOTE: func ($piyo --> Str)func ($piyo) returns Str と書くこともできます

  1. $x, $y, $z の3つの変数を受け取ります
  2. ($x, "時の", $y, "は", $z)というリストを生成し、.joinで要素を結合し、文字列を返します

08. 暗号文

コード

08.p6
cipher("abc東北123").say;

my sub cipher(Str $text --> Str) {
    gather for $text.comb {
        if $_ ~~ /<:Ll>/ {
            take 219 - $_.ord
        } else {
            take $_
        }
    }.join.Str
}

解説

sub cipher(Str $text --> Str)

  1. gather for take イディオムを用いて遅延リストを生成します
    1. $text.comb$textを文字単位に分割してリストにし、ひとつひとつの文字を見ていきます
    2. 与えられた文字が<:Ll> つまり英小文字だった場合は 219 - その文字の文字コード を取得します
    3. そうでない場合はそのまま文字を取得します

09. Typoglycemia

コード

09.p6
"I couldn't believe that I could actually understand what I was reading : the phenomenal power of the human mind ."\
.words\
.map({ my @chars = .comb;
       if @chars.elems <= 4 {
           @chars.join
       } else {
           @chars.head ~ @chars[1..^@chars.end].pick(@chars - 2).join ~ @chars.tail
       }
     })\
.say;

解説

  1. .words でinvocantの文字列を空白区切りで分割し、リストにします
  2. .mapでinvocantのリスト中の各単語を操作します
    1. 単語を文字単位で分割し、リストにします
    2. 文字数が4以下(i.e. 1.のリストの要素数が4以下)ならそのまま結合して文字列にします
    3. そうでないなら、@chars中の[先頭+1,末尾)の箇所から .pick を使ってランダムな順番で文字列を選び、joinにより結合します。そして、この文字列に対して、@chars.head を先頭に、@chars.tail を末尾に結合します。
  3. .say で出力します

補足

  • 興味のある方はpickrollの違いを公式ドキュメントで調べてみましょう

以上、Perl 6でNLP 5本ノック 『第1章: 準備運動』 05 ~ 09 でした。

0
0
0

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
0
0