8
7

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 5 years have passed since last update.

PowerShellで言語処理100本ノック 第1章~第3章

Last updated at Posted at 2019-02-26

言語処理100本ノック 2015

第1章: 準備運動

00. 文字列の逆順

文字列 "stressed" の文字を逆に(末尾から先頭に向かって)並べた文字列を得よ.

$s = "stressed"
-join $s[$s.length..0]

01. 「パタトクカシーー」

パタトクカシーー」という文字列の1,3,5,7文字目を取り出して連結した文字列を得よ.

-join "パタトクカシーー"[0,2,4,6]

02. 「パトカー」+「タクシー」=「パタトクカシーー」

パトカー」+「タクシー」の文字を先頭から交互に連結して文字列「パタトクカシーー」を得よ.

$enmr = "パトカー","タクシー" | foreach { ,$_.getenumerator() }
-join $(while(!($enmr.movenext() -eq 0).count) { $enmr.current })

03. 円周率

"Now I need a drink, alcoholic of course, after the heavy lectures involving quantum mechanics." という文を単語に分解し,各単語の(アルファベットの)文字数を先頭から出現順に並べたリストを作成せよ.

$s = "Now I need a drink, alcoholic of course, after the heavy lectures involving quantum mechanics."
$s -split "[., ]+" | foreach length

04. 元素記号

"Hi He Lied Because Boron Could Not Oxidize Fluorine. New Nations Might Also Sign Peace Security Clause. Arthur King Can." という文を単語に分解し,1, 5, 6, 7, 8, 9, 15, 16, 19番目の単語は先頭の1文字,それ以外の単語は先頭の2文字を取り出し,取り出した文字列から単語の位置(先頭から何番目の単語か)への連想配列(辞書型もしくはマップ型)を作成せよ.

$s = "Hi He Lied Because Boron Could Not Oxidize Fluorine. New Nations Might Also Sign Peace Security Clause. Arthur King Can."
$s.split() | foreach { $i = 1; $h = [ordered]@{} } { $h[$_.substring(0, ($i++ -notin 1,5,6,7,8,9,15,16,19) + 1)] = $i } { $h }

05. n-gram

与えられたシーケンス(文字列やリストなど)からn-gramを作る関数を作成せよ.この関数を用い,***"I am an NLPer"***という文から単語bi-gram,文字bi-gramを得よ.

function ngram($p, $n) {
    $spr = if ($p -is "string") { "" } else { " " }
    0..([math]::max($p.length - $n, 0)) | foreach { $p[$_..($_ + $n - 1)] -join $spr } | select -u
}

$s = "I am an NLPer"
ngram $s 2
ngram $s.split() 2

06. 集合

"paraparaparadise""paragraph" に含まれる文字bi-gramの集合を,それぞれ, XYとして求め,XYの和集合,積集合,差集合を求めよ.さらに,'se' というbi-gramがXおよびYに含まれるかどうかを調べよ.

$x = ngram "paraparaparadise" 2
$y = ngram "paragraph" 2

"union"
$x + $y | select -u
"intersection"
$x | where { $_ -in $y }
"difference (x-y)"
$x | where { $_ -notin $y }

gv x,y | foreach { "{0} contains se:{1}" -f $_.name, ("se" -in $_.value) }

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

引数 x, y, z を受け取り「x 時の y は z」という文字列を返す関数を実装せよ.さらに,x=12, y="気温", z=22.4 として,実行結果を確認せよ.

function getstr($x, $y, $z) { "${x}時の${y}${z}" }

getstr -x:12 -y:気温 -z:22.4

08. 暗号文

与えられた文字列の各文字を,以下の仕様で変換する関数 cipher を実装せよ.

  • 英小文字ならば( 219 - 文字コード )の文字に置換
  • その他の文字はそのまま出力

この関数を用い,英語のメッセージを暗号化・復号化せよ.

filter cipher { [regex]::replace($_, "[a-z]", { 219 - $args.value[0] -as "char" }) }

"Hello World!" | cipher
"Hello World!" | cipher | cipher

09. Typoglycemia

スペースで区切られた単語列に対して,各単語の先頭と末尾の文字は残し,それ以外の文字の順序をランダムに並び替えるプログラムを作成せよ.ただし,長さが以下の単語は並び替えないこととする.適当な英語の文(例えば***"I couldn't believe that I could actually understand what I was reading : the phenomenal power of the human mind ."***)を与え,その実行結果を確認せよ.

$s = "I couldn't believe that I could actually understand what I was reading : the phenomenal power of the human mind ."
[regex]::replace($s, "(?<=\b\S)\S{3,}(?=\S\b)", { -join ($args.value -as "char[]" | sort { random }) })                #

第2章: Linuxコマンド

hightemp.txt は,日本の最高気温の記録を「都道府県」「地点」「℃」「日」のタブ区切り形式で格納したファイルである.以下の処理を行うプログラムを作成し,hightemp.txt を入力ファイルとして実行せよ.さらに,同様の処理をUNIXコマンドでも実行し,プログラムの実行結果を確認せよ.

エンコード変換

毎回エンコード指定するのが面倒なのでUTF16LEに変換しておく。

(gc hightemp.txt -enc utf8) | sc hightemp.txt

10. 行数のカウント

行数をカウントせよ.

(gc hightemp.txt).count

11. タブをスペースに置換

タブ1文字につきスペース1文字に置換せよ.

(gc hightemp.txt).replace("`t", " ")

12. 1列目をcol1.txtに,2列目をcol2.txtに保存

各行の1列目だけを抜き出したものを col1.txt に,2列目だけを抜き出したものを col2.txt としてファイルに保存せよ.

gc hightemp.txt | cfs | foreach { $_.p1 >> col1.txt; $_.p2 >> col2.txt }

13. col1.txtとcol2.txtをマージ

12で作った col1.txtcol2.txt を結合し,元のファイルの1列目と2列目をタブ区切りで並べたテキストファイルを作成せよ.

gc col1.txt,col2.txt | group readcount | foreach { $_.group -join "`t" } > merged.txt

14. 先頭からN行を出力

自然数 N をコマンドライン引数などの手段で受け取り,入力のうち先頭の N 行だけを表示せよ.

$n = 3
gc hightemp.txt -head $n

15. 末尾のN行を出力

自然数 N をコマンドライン引数などの手段で受け取り,入力のうち末尾の N 行だけを表示せよ.

gc hightemp.txt -tail $n

16. ファイルをN分割する

自然数 N をコマンドライン引数などの手段で受け取り,入力のファイルを行単位で N 分割せよ.

function fsplit($path, $n) {
    $c = (gc $path | measure).count
    $rc = [math]::floor($c / $n)
    $head = $c % $n * ($rc + 1)
    @{ r = $rc + 1; head = $head; }, @{ r = $rc; tail = $c - $head } |
    foreach { $i = 1 } { gc $path @_ | foreach { $_ > ("part{0}.txt" -f $i++) } }
}

fsplit hightemp.txt $n

17. 1列目の文字列の異なり

1列目の文字列の種類(異なる文字列の集合)を求めよ.

gc hightemp.txt | cfs | select p1 -u

18. 各行を3コラム目の数値の降順にソート

各行を3コラム目の数値の逆順で整列せよ(注意: 各行の内容は変更せずに並び替えよ).

gc hightemp.txt | sort { $_.split()[2] -as "double" } -d

19. 各行の1コラム目の文字列の出現頻度を求め,出現頻度の高い順に並べる

各行の1列目の文字列の出現頻度を求め,その高い順に並べて表示せよ.

gc hightemp.txt | cfs | group p1 | sort count -d

第3章: 正規表現

Wikipediaの記事を以下のフォーマットで書き出したファイル jawiki-country.json.gz がある.

1行に1記事の情報がJSON形式で格納される
各行には記事名が "title" キーに,記事本文が "text" キーの辞書オブジェクトに格納され,そのオブジェクトがJSON形式で書き出される
ファイル全体はgzipで圧縮される
以下の処理を行うプログラムを作成せよ.

20. JSONデータの読み込み

Wikipedia記事のJSONファイルを読み込み,「イギリス」に関する記事本文を表示せよ.問題21-29では,ここで抽出した記事本文に対して実行せよ

$s = gc jawiki-country.json -enc utf8 | ConvertFrom-Json | where title -eq "イギリス" | foreach text

21. カテゴリ名を含む行を抽出

記事中でカテゴリ名を宣言している行を抽出せよ.

$s.split("`n") | sls -s "[[Category:"

22. カテゴリ名の抽出

記事のカテゴリ名を(行単位ではなく名前で)抽出せよ.

[regex]::matches($s, "(?<=\[\[Category:).+?(?=\||\])").value

23. セクション構造

記事中に含まれるセクション名とそのレベル(例えば"== セクション名 =="なら1)を表示せよ.

[regex]::matches($s, "=(=+)(.+?)\1=") | foreach { $_.groups[1].length, $_.groups[2] -join ":" }

24. ファイル参照の抽出

記事から参照されているメディアファイルをすべて抜き出せ.

[regex]::matches($s, "(?<=\[\[(ファイル|File):).*?(?=\|)").value

25. テンプレートの抽出

記事中に含まれる「基礎情報」テンプレートのフィールド名と値を抽出し,辞書オブジェクトとして格納せよ.

function Get-BasicInfo($text) {
    [regex]::match($text, "((?'o'{)|(?'c-o'})|.)*","s").groups["c"].captures.value |
    where { $_.startswith("基礎情報") } | foreach { $_ -split "\n\||\|\n" } | select -skip 1
}

function ConvertTo-HashTable {
    Begin   { $h = [ordered]@{} }
    Process { $k, $v = $_ -split "=", 2 | foreach trim; $h[$k] = $v }
    End     { $h }
}

Get-BasicInfo $s | ConvertTo-HashTable

26. 強調マークアップの除去

25の処理時に,テンプレートの値からMediaWikiの強調マークアップ(弱い強調,強調,強い強調のすべて)を除去してテキストに変換せよ(参考: マークアップ早見表

filter Remove-Emphasis { $_ -replace "'('+)(.+?)\1'", '$2' }

Get-BasicInfo $s | Remove-Emphasis | ConvertTo-HashTable

27. 内部リンクの除去

26の処理に加えて,テンプレートの値からMediaWikiの内部リンクマークアップを除去し,テキストに変換せよ.

filter Remove-InternalLink { $_ -replace "(#REDIRECT )?\[\[([^\]]+?\|)*([^\]]+?)\]\]", '$3' }

Get-BasicInfo $s | Remove-Emphasis | Remove-InternalLink | ConvertTo-HashTable

28. MediaWikiマークアップの除去

27の処理に加えて,テンプレートの値からMediaWikiマークアップを可能な限り除去し,国の基本情報を整形せよ.

filter Remove-Template { $_ -replace "{{([^}]+?\|)*([^}]+?)}}", '$2' }
filter Remove-HtmlTag { $_ -replace "(?:<([a-z]+)[ >][\s\S]*?</\1>)|(<[a-z] ?[^<>]*?/>)|(<\!--.+?-->)" }

$h = Get-BasicInfo $s | Remove-Emphasis | Remove-Template | Remove-InternalLink | Remove-HtmlTag | ConvertTo-HashTable

29. 国旗画像のURLを取得する

テンプレートの内容を利用し,国旗画像のURLを取得せよ.(ヒント: MediaWiki APIの imageinfo を呼び出して,ファイル参照をURLに変換すればよい)

irm https://en.wikipedia.org/w/api.php -b @{
    action = "query"
    format = "json"
    prop = "imageinfo"
    iiprop = "url"
    titles = "File:" + $h["国旗画像"]
} | foreach { $_.query.pages.psobject.properties.value.imageinfo.url }
8
7
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
8
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?