y_uti のブログ

統計、機械学習、自然言語処理などに興味を持つエンジニアの技術ブログです

『言語処理 100 本ノック』に PHP で挑む (問題 06 ~ 09)

『言語処理 100 本ノック』に PHP で挑戦しています。今回は問題 06 から第一章の残りを解いていきます。前回の記事で解いた問題 05 では n-gram という概念が登場しましたが、今回の問題には言語処理特有の話題はなく、いずれも単純なプログラミングの問題になっています。
www.cl.ecei.tohoku.ac.jp

06. 集合

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

<?php
require_once __DIR__ . '/character_ngram.php';

$input1 = 'paraparaparadise';
$input2 = 'paragraph';

$bigram1 = character_ngram($input1, 2);
$bigram2 = character_ngram($input2, 2);

$set1 = array_flip($bigram1);
$set2 = array_flip($bigram2);
echo json_encode(array_keys($set1)), "\n";      // ["pa","ar","ra","ap","ad","di","is","se"]
echo json_encode(array_keys($set2)), "\n";      // ["pa","ar","ra","ag","gr","ap","ph"]

$union = array_merge($set1, $set2);
echo json_encode(array_keys($union)), "\n";     // ["pa","ar","ra","ap","ad","di","is","se","ag","gr","ph"]

$intersect = array_intersect_key($set1, $set2);
echo json_encode(array_keys($intersect)), "\n"; // ["pa","ar","ra","ap"]

$diff = array_diff_key($set1, $set2);
echo json_encode(array_keys($diff)), "\n";      // ["ad","di","is","se"]

連想配列のキーを利用して集合を表現しました。和集合、積集合、差集合はそれぞれ array_merge, array_intersect_key, array_diff_key で計算できます。

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

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

<?php
function template($x, $y, $z)
{
    return "${x}時の${y}${z}";
}

echo template(12, '気温', 22.4), "\n"; // 12時の気温は22.4

プログラミング言語によっては数値から文字列への変換が必要になりますが、PHP では特に気にする必要はありません。

08. 暗号文

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

  • 英小文字ならば(219 - 文字コード)の文字に置換
  • その他の文字はそのまま出力
この関数を用い,英語のメッセージを暗号化・復号化せよ.

<?php
function cipher($sentence)
{
    $len = strlen($sentence);
    $result = '';
    for ($i = 0; $i < $len; ++$i) {
        $c = $sentence[$i];
        $result .= 'a' <= $c && $c <= 'z' ? chr(219 - ord($c)) : $c;
    }
    return $result;
}

$input = file_get_contents('php://stdin');

$ciphered = cipher($input);
echo $ciphered;

$deciphered = cipher($ciphered);
echo $deciphered;

英小文字に対して 219 から文字コードを引いたものは、"a" が "z" に、"z" が "a" にと逆順に対応するので、cipher 関数で暗号化された文字列をもう一度 cipher 関数に与えれば復号化できます。この問題では入力文が具体的に与えられていないので、標準入力を受け取るように実装しました。

09. Typoglycemia

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

<?php
function shuffle_word($word)
{
    $len = strlen($word);
    if ($len <= 4) {
        return $word;
    }

    $chars = str_split(substr($word, 1, $len - 2));
    shuffle($chars);
    $result = $word[0] . implode($chars) . $word[$len - 1];

    return $result;
}

$sentence = 'I couldn\'t believe that I could actually understand what I was reading : the phenomenal power of the human mind .';
$answer = implode(' ', array_map('shuffle_word', explode(' ', $sentence)));
echo $answer, "\n";

最後の問題も特に難しいところはありません。