きょうこログ

初心忘れず、最後まで全力で頑張ります。よろしくお願いします。

2年前の高専ロボコンでチームリーダーをした記録

2014年から2016年まで、明石高専ロボット工学研究部 ( = ロボ研 ) に在籍し、NHKロボコンに参加していました。高専に入学してからの3年間をロボ研で過ごして、4年目の高専生活も終え、就職を1年後に控えた今、ロボコンを通じて考えた事や学んだ事を記録を残しておきたいと思います。とは言ってもロボコンの技術的な内容は、いつか使わなくなってしまうので… この記事のテーマは「2年前の高専ロボコンでチームリーダーをした記録」です。

上手くまとめられるかな… 文章の内容や書き方などに疑問点があれば、ご連絡頂けると凄く嬉しいです。どうぞよろしくお願いします。

そもそも私達はどんなロボットを作ったのか

2年前 (2016年) の高専ロボコンのテーマは「ロボット・ニューフロンティア」でした。ルールは簡単に言うと「箱運び & 箱積み」です。詳しい内容は当時のルールブックをご覧ください。

今年の競技はロボットによる「新大陸開拓」です。

みなさんは「探検家」として新大陸を目指します。待ち受ける障害をロボット で乗り越え、新大陸を開拓した証としてブロックを積み上げ「砦」を築き上げ ます。

高専ロボコン2016ルールブック

私が所属していたのは、明石高専Bチーム「あさごん」です。私達のチームでは数々の試作を経て、バンブー田城 (写真左) や カストーレ竹田 (写真右) 、橋の わたる と とおる を制作しました。… 凄く懐かしい名前ですね笑 下から箱を滑り込ませる機構や大容量の箱運びが特徴のロボット達です。

f:id:f_respectful12:20180414193133j:plain


ちょっとだけ寂しかった経験

私が高専に入学したのは2014年の4月。その頃はロボコンなんて1mmも知りませんでしたが、当時の先輩が動かして見せてくれた 球を飛ばすロボット ( 多分、2014年の近畿合ロボに出したロボット ) にうっかり心を揺さぶられてしまい、そのまま ロボ研への入部を決めてしまいました。

ですが、実際に入部したロボ研は、想像以上に(私にとって)キツい環境で、ちょっとだけ寂しさを感じていました。具体的には、

  1. 女子が圧倒的に少ない(後期開始の時点で1人)
  2. 機械工学科の人が大半で電気情報工学科の私には アウェー
  3. 同級生はロボコン経験者が多く、知識量で追いつけない
  4. そして 技術的にも追いつけない
  5. そもそも高専生的な知識 (アニメとか数学とか) に乏しくて 普通の会話に混ざれない
  6. 1年間でかなりの数の部員が辞めていく

などです。技術的なトコロでロボ研の力になれない上、他の部員の方ともイマイチ仲良くなりきれない状況は、なかなか辛いものがあり、実際にロボ研を辞めていった同級生達も同じ気持ちだったんじゃないかなぁ…と感じています。また最初の1年間で、辞めてしまった上級生の方々が何人もいらっしゃって、もしかして部活楽しくなかったのかな…?と考えたりもしていました。

ロボ研1年目を終えて、私がロボコンにおいて1番重要だと思ったのは「いかにロボコンを楽しむか」です。楽しい学校生活にする為に楽しいロボコンをしたかったことも理由の1つですが、何より私が初めて見た 2014年の全国高専ロボコン優勝されたチーム の凄く楽しそうな様子が印象的だったからです。

f:id:f_respectful12:20180415174959p:plain

チームリーダーとして目指した環境

そんな私も、なんやかんやで3年生に進級し、ロボコンのチームリーダーを担当するまでになりました。チームリーダとして1番に目指したのは「全員が仲良く楽しく真剣に全力を出せる環境作り」です。そんな環境作りの為に、絶対に外せない要素として、

  • 初心者の1年生に壁を感じさせない
  • ベテランの上級生にストレスを感じさせない
  • スケジュールを可視化する
  • 完成形のイメージを共有する
  • (ロボコンと関係なく) チームメイトとの親交を深める
  • 自分のストレスはチームメイトに見せない
  • とりあえず色んなメンバーと喋りまくる

を意識してチームを作りました。

…ご存知の方もいらっしゃるかもしれませんが、この時の明石高専Bチームの目標は「絶対全国優勝 ~ All of us make the aim perfect! ~」です。チーム内やメディアの前では「絶対全国優勝」の方を多く口にしていたのですが、実は同じぐらい「 All of us make the aim perfect! 」も大切にしていました。意味は「チーム全員で目標を成し遂げよう!」です。(正しい英語なのかは微妙…)"All of us" ( チーム全員 ) っていう言葉がポイントで、1人でも多くロボコンを真剣に楽しむ人が増えるように 心がけていました。


以下はチーム作りの中でも特に大事にした部分です。

1. 初心者の1年生に壁を感じさせない

具体的な方策

  • 基本的に話し合いや会議では専門用語を使わない
  • 可能な限り報告会を開催しロボコンの流れを感じてもらう

高専ロボコンに参加する1年生は、

  1. ロボコン経験者 or やたら技術的な事をよく知ってる子
  2. ロボコン未経験者 or 技術はまだ分からないけどロボコンに興味がある

といった2つのタイプに分かれることが多いです。ちなみに私は 2. の方でした。

少し話が変わりますが、高専に入学する全ての人は多かれ少なかれ「自分よりもよく出来る人が近くにいる」と感じたことがあるんじゃないかと思います。私はそうでした。自分には付いていけない専門的な話で盛り上がる同級生や先輩を見て、微妙に劣等感を感じていました。

私がチームリーダーになって最初に始めたのは、この 1.2. どちらのタイプの1年生にも壁や劣等感を感じさせずロボコンを楽しんでもらうことです。1年生がロボコン楽しみながら積極的に活動に参加してくれると、単純に作業人員が確保できます。その上、私の居たチームでは純粋にロボコンを楽しむ1年生に引っ張られて上級生も楽しく作業していたんじゃないかと思っています。

2. スケジュールを可視化する

f:id:f_respectful12:20180423170740p:plain

当時はLINEのノート機能を使ってこんな風にスケジュールをチームで共有していました。今はもっといいアプリや機能があると思いますが、どんなものを使うにせよ チームメンバー全員でスケジュールを確認し合える環境 は必要だったと思います。

ちなみに、これらのスケジュールは私 ( = チームリーダー ) だけで決めていた訳ではありません。定期的に3年生 ( = 当時のチームの最高学年 ) に集まってもらって、スケジュールや全体の流れを確認して貰ってから決定していました。

3. 完成形のイメージを共有する

f:id:f_respectful12:20180423171107p:plain

これは6月頃に設計前の段階で描いたロボットの凄くざっくりした計画図です。試作で決定した機構がどこに使用されるか。他の機構との兼ね合いはどうか。をチームで共有するために書きました。

ロボコンには加工、設計、回路、制御 etc... 様々な役割があります。その分チームのメンバーがそれぞれ持っている専門知識や経験も様々です。そんな色んな役割の人達が1つのものを作る為には 同じ ゴールイメージ が見えていないと難しいんじゃないかなぁ〜と思っています。その点では( 微妙な完成度のざっくりした計画図でも )色んな役割・学年の人が 同じ図を見てイメージを共有できて、良かったんじゃないかと思います。

4. 自分のストレスはチームメイトに見せない

大会が近づいてくると、メンバー同士の口調のきつい議論が増えたり、チーム全体がイライラした雰囲気に包まれることは、どんなチームであっても少なからずあると思います。( ロボットの調子が悪い時にも起こりがち… )

私がここでチームリーダーとして大事にしたのは「いかにメンバーが余裕を持って作業できるか」です。その為には私の感じていたストレスは外に出さず、出来るだけ余裕を持ってチーム内ではいつでもニヤニヤしているように、心がけていました。( 本当に出来ていたかどうかは怪しい )

また、チームの一部で切羽詰まった議論が発生した場合は、一度その場で議論を停止してもらい、落ち着いた後にチーム全員で話し合いを行いました。チームに発生した問題を全体に共有できるだけでなく、全員で落ち着いて話し合いが出来ると後からわだかまりとして残らないので、良かったと思います。


以上が私の「2年前の高専ロボコンでチームリーダーをした記録」です。本当はもう少し、心の中でこっそり考えていたことや意識して行動していたことがあるのですが、恥ずかしいのでこの位にしておきます…!

こんな事を書くと怒られてしまいそうですが、まずは楽しんでみるっていうのも、1つのアイデアなんじゃないかなぁと思います。もちろんロボコンにしかない自由な発想も高専でしか身につけられない技術も大事だと思いますが、あまり求められているものに縛られ過ぎず、楽しくロボコンをする人が増えれば嬉しいなーって思います。こんなまとめで伝わるかな…笑

…いつか書こうと思っていたこの記事もようやく書き終わり、ようやく私の中のロボコンは一区切りつきました。チームリーダーをした1年間も含めてロボコンと関わった3年間は、色んな事が出来て色んな人に出会えて、凄くいい経験になりました。これからもロボコンが続いていくんだろうなぁって考えると、嬉しいような寂しいような… 本当に有難うございました。

現役の ( +未来の? ) ロボコニストの皆さんこれからも頑張ってください!私も頑張るぞー!

おまけ

当時の部長が作ってくださったロボットの紹介PVです。面白くてめちゃめちゃ気に入ってます。しかもめっちゃいい声。よかったら是非見てみてください!

言語処理100本ノック【第3章 前編】

課題研究で言語処理100本ノック 2015に取り組んでいます。前回の続きです。使用言語はPython3です。今回からは第3章 正規表現 です。まずは第3章前半の5つを説明します。よければプログラムも説明文も、間違ったところや変なところがあればアドバイス頂けるとすごく嬉しいです!

第3章の概要

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

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

20. JSONデータの読み込み

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

ソースコード

import json

with open("jawiki-country.json", "r") as f:
  for i in f:
    a = json.loads(i) 
    if a.get("title") == "イギリス":
      print(a)

このプログラムではJSONファイルを読み込みの前段階として、withを使用しています。これまでのプログラムでは、open()でファイルオブジェクトを取得しclose()でメモリを解放する ( = ファイル操作を終わる ) までが一連の流れでした。しかしwith open() asを使ってファイルオブジェクトを取得すると、withブロックを抜けるタイミングで自動的にcloseするので、close()を明示する必要がありません。めっちゃ便利。

JSONデータを読み込むには、jsonモジュールのloads関数を用います。そもそもJSONとはデータを表現するための記法の1つで、難しい構造のデータを人間にも分かり易く表現することができます。例えば、

{
    "名前":[
        "きょうこ",
        "KYOKO",
    ],
    "出身地" : "日本",
    "生年月日":[
        "" : "1998",
        "" : "12",
        "" : "26"
    ]
}

と書くことで、人物のデータをまとめることが出来ます。loads関数を用いることで、与えられたJSONデータを辞書型に変換しました。

出力結果

{{redirect|UK}}
{{基礎情報 国
|略名 = イギリス
|日本語国名 = グレートブリテン及び北アイルランド連合王国
(中略)
[[Category:島国|くれいとふりてん]]
[[Category:1801年に設立された州・地域]]

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

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

ソースコード

import json

with open("jawiki-country.json", "r") as f:
  for i in f:
    a = json.loads(i) 
    if a.get("title") == "イギリス":
      b = a.get("text").split("\n")

for i in b:
  if "Category" in i:
    print(i)

split()は文字列を任意の区切り文字で分割する関数で、このプログラムではイギリスに関する記事を \n ごとに区切りました。区切った各行に対してinステートメントを使って、"Category" という文字列を含む行のみを抽出しています。

実行結果

[[Category:イギリス|*]]
[[Category:英連邦王国|*]]
[[Category:G8加盟国]]
[[Category:欧州連合加盟国]]
[[Category:海洋国家]]
[[Category:君主国]]
[[Category:島国|くれいとふりてん]]
[[Category:1801年に設立された州・地域]]

22. カテゴリ名の抽出

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

ソースコード

import json
import re

with open("jawiki-country.json", "r") as f:
  for i in f:
    a = json.loads(i) 
    if a.get("title") == "イギリス":
      b = a.get("text").split("\n")

regex = re.compile("^\[\[Category:(.*?)(\|.*)*\]\]$")

for j in b:
  if "Category" in j:
    print(regex.search(j).group(1))

このプログラムでは、jsonモジュールとreモジュールをインポートしました。1つ前の 21. カテゴリ名を含む行を抽出 で抽出した行に対して正規表現を使用し、カテゴリ名を抜き出します。re.compile()は、正規表現オブジェクトをコンパイルする関数です。この関数で生成したオブジェクトに対して、reモジュールの関数を使用することができます。

この問題のポイントは、行によって抽出のパターンが2通り考えられることです。例えば、

f:id:f_respectful12:20180405150051p:plain

のように、パターン①では |* を除去して "イギリス" のみ抽出する必要がありますが、パターン②では "欧州連合加盟国" だけを抽出します。ここでは、"|"からはじまる0文字以上の文字を考えることで、2つのパターンから必要な情報だけを抜き出しました。

^[[Category:(.?)(|.)*]]$

正規表現 意味
^ 行のはじまり
[[Category: 全て一致
(.*?) 任意の0文字以上の文字: グループ
(|.*) "|"からはじまる0文字以上の文字: グループ
*]] 全て一致
$ 行のおわり

search()正規表現がマッチする最初の場所を探す関数です。正規表現オブジェクト.search(検索対象の文字列)で検索結果 ( = マッチオブジェクト ) を返します。group()はマッチオブジェクトを解読して、抽出したグループを取り出す関数です。正規表現では抽出対象をグループ化することが出来ます。この例では、(.*?)(\|.*)などです。group()の引数にはグループ番号を指定します。グループ番号は正規表現の左から順番に 1, 2, 3... です。抽出対象全体は 0 で指定できます。

実行結果

イギリス
英連邦王国
G8加盟国
欧州連合加盟国
海洋国家
君主国
島国
1801年に設立された州・地域

23. セクション構造

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

ソースコード

import json
import re

with open("jawiki-country.json", "r") as f:
  for i in f:
    a = json.loads(i) 
    if a.get("title") == "イギリス":
      b = a.get("text").split("\n")

regex = re.compile(u"^(==+)\s*(.*?)\s*(==+)$")

for j in b:
  c = regex.search(j)
  if c != None: print("{0} [{1}]".format(c.group(2), len(c.group(1)) - 1))

このプログラムでは、==国名====歴史==といった風に与えられているセクション名から構造を読み解き、構造の深さを数値化して出力します。

f:id:f_respectful12:20180405161602p:plain

ここで使用した正規表現は以下の通りです。

^(==+)\s(.?)\s*(==+)$

正規表現 意味
^ 行のはじまり
(==+) "=="と1回以上一致する"="だけで構成される文字
\s* 任意の0文字以上の空白文字
(.*?) 任意の0文字以上の文字
\s* 任意の0文字以上の空白文字
(==+) "=="と1回以上一致する"="だけで構成される文字
$ 行のおわり

2番目のfor文では、上記の正規表現と一致するときセクション名と構造の深さを出力します。group(1)の長さから1引いたものを、構造の深さと考えました。

実行結果

国名 [1]
歴史 [1]
地理 [1]
気候 [2]
政治 [1]
外交と軍事 [1]
地方行政区分 [1]
(中略)
関連項目 [1]
外部リンク [1]

24. ファイル参照の抽出

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

ソースコード

import json
import re

with open("jawiki-country.json", "r") as f:
  for i in f:
    a = json.loads(i) 
    if a.get("title") == "イギリス":
      b = a.get("text").split("\n")

regex = re.compile("(File|ファイル):(.*?)\|")

for j in b:
  c = regex.search(j)
  if c != None:
    print(c.group(2))

記事から参照されているメディアファイルは、

|国章画像 = [[ファイル:Royal Coat of Arms of the United Kingdom.svg|85px|イギリスの国章]]

[[File:Battle of Waterloo 1815.PNG|thumb|left|[[ワーテルローの戦い]]での勝利により[[ナポレオン戦争]]は終止符が打たれ、[[パクス・ブリタニカ]]の時代が到来した。]]

のように、ファイル:もしくはFile:| で囲まれた範囲に記されていることが分かります。これから考えられる正規表現は、

(File|ファイル):(.*?)|

正規表現 意味
(File ファイル) |"ファイル"もしくは"File"と一致
: 全て一致
(.*?) 任意の0文字以上の文字
| 全て一致

です。

実行結果

Royal Coat of Arms of the United Kingdom.svg
Battle of Waterloo 1815.PNG
The British Empire.png
Uk topo en.jpg
BenNevis2005.jpg
Elizabeth II greets NASA GSFC employees, May 8, 2007 edit.jpg
(中略)
London Tower (1).JPG
Wembley Stadium, illuminated.jpg

エントリーシートに時間を取られてたら、いつの間にか高専5年生になっていました。添削して下さった方々、本当にありがとうございます。 学生最後の1年間、インプットもアウトプットも楽しみまくるぞー!

面白法人カヤックさんとブレスト面談をさせて頂いた記録

3月5日から3月9日までの1週間、面白法人カヤック さんのところへ技術部インターンに行ってきました。場所は鎌倉にある本社ではなく、私の地元神戸の上位互換とも言われる横浜にある綺麗なオフィスです。オルタナティ部 というデバイス制作寄りの部署に配属されたため、インターン期間中はArduinoを主に触っていました。

今回のインターンに関して書きたい事は山程ありますが、その中でも1番感動&興奮した ブレスト面談 について、記録を残しておきたいと思います。

※まだ文章を書く練習をしている最中なので、もしかしたら文章の途中に変な表現や良くない表現が混じっているかもしれません。その時はご連絡頂けると凄く嬉しいです。どうぞよろしくお願いします。

そもそも私について

現在は、兵庫県の瀬戸内海が見える 明石高専 というところの 電気情報工学科 で勉強しています。2014年から2016年まで NHK高専ロボコン に参加していました。高専に入るまでは ゲームを作るプログラマーを漠然と目指していましたが、ロボコンも含めた高専での経験を経て、現在は開発チームのディレクターを最終目標に就職活動をしています。

実はちょうど1年前に企画部として、カヤックさんの所へインターンに行ったことがあります。その時凄く面白かった経験があって、今後のキャリアを考えつつ、技術部として再びインターンに参加するに至りました。

面白法人カヤックさんについて

つくる人を増やす」を経営理念に、面白いこと(を主軸に色んなこと)をされている会社です。雑な説明になってしまいましたが、HPをご覧頂くと、雰囲気が掴めるような気がします。インターンでお世話になった方も含めて、カヤックさんのもとで働く方々は、凄く親切で凄く楽しそうでした。しかも、何かしらでアウトプットを残している方ばかりです。

カヤックさんの Vision の1つに、「始まりも終わりもブレスト」と言うものがあって、ほとんど1日中社内の各所でブレストが行われていました。 私が今回この記事で書きたい ブレスト面談 も、恐らくここに関連するところです。

ブレスト面談の記録

ブレスト面談とは、就活生や内定者と実際にカヤックで働く方々が、進路や配属の希望などを ブレインストーミング を通して 解決方法を導いたり明らかにする面談のことだそうです。

インターン最終日に私のこれからのキャリアプランを相談させて頂いたところ、私の担当をして下さった方がブレストが得意な方を呼んで、場をセッティングしてくださいました。

私の現在のキャリアプランは、もの凄くざっくり言うと下の図のような感じです。最終目標である "凄いディレクター" は、世界的に愛されるコンテンツやプロダクトを作るチームをディレクションすることを1つの目安にしています。

f:id:f_respectful12:20180310055013p:plain

そこでカヤックの方々は私をクライアントと想定し、現在の私が最終目標を達成するための方法について、ブレストをすることで沢山のアイデアを提案してくださいました。

f:id:f_respectful12:20180310065244p:plain

1時間程のブレスト面談を終えた結果はこのような感じです。

f:id:f_respectful12:20180310201332j:plain

ブレストの内容をまとめると…

目標までの最短ルート上にある企業に入社する方法は3つある

  • 新卒採用
    • 希望の職種に就くまでに時間がかかる可能性が高い
    • 倍率が高い
  • 中途採用 or MBO
    • 今までしてきたことを生かした働き方ができる可能性が高い
    • さらにMBOなら理想的

MBO ( = 企業に買収されること ) される為には、どこかで目に止まる ( = バズる? )ことが必要で、その為に今から面白いアイデアや新しい遊びを考えて公開してみるのがいいんじゃないか

例えば…

  • YouTubeに作ったものを投稿する
  • 自分で作ったものを自分で実況する( 自作自演 )
  • Twitterで1日1ゲーム

コンスタントに遊びやアイデアを出せるようになる為には練習が必要

せっかくなら今まで経験してきたロボコンや制御的な要素を加えられるとなおいい


そもそも新しい遊びを考える手法も大きく3つある

  • ゴールのイメージを先行して決める
  • 突然ひらめく
  • 今あるものを組み合わせる
    • この方法が1番の主流
    • 面白いもの × みんなが知っているもの

他にも、元のアイデアから切り出したり、色んな人向けに拡張したりすることで、全く別の遊びと見なせることもある。

面白さを分析する為に今あるルールをまとめてみるのも効果的

例えば…世の中の遊びは大きく4つの要素に分けられる

  • 競争 : かけっこ, ランキング
  • 運試し : じゃんけん, ガチャ
  • めまい : ブランコ, ジェットコースター
  • 真似事 : ままごと

これらの要素をバランスよく組み合わされている

ブレスト面談を終えて

正直に言うとここまで話が広がって、ここまで沢山のアイデアを頂けるとは思っていませんでした。私もロボコンのアイデア会議だったり、Co+work の授業でアイデアを出すことは何度かあったのですが、日常生活の一部にも活用したのは初めてです。今までの私はとりあえず前進する事ばかり考えていて、1度立ち止まって方針を考えてみるだけで、思考がクリアになることも衝撃でした。

反省点を挙げるとするなら、ブレスト面談の記録をもっとしっかりとっておけば良かったなぁというところと、ブレストの技術がまだまだ足りなかったなぁというところです。ただブレストの技術に関しては、カヤックで働く方々は、日常の会話でもお仕事でも沢山ブレストをされていて、環境や経験値によっても変わってくるものがあるんじゃないかと思いました。凄かった。

いつもの癖で気持ちだけで喋ってしまう場面も沢山あったのですが、優しくフォローしてくださった方々、本当に有難うございました。

今後の作戦

高専で専門的なことを勉強してきましたが、私の最終目標はエンジニアではなくディレクターです。ただ、エンジニアとしての経験も私がディレクターを目指す上では必要だと思っているので、残り1年の高専生活は、今の研究内容以外のエンジニア的スキルを身につけつつ、アイデアを出す練習や面白さを見つける練習をするのが最善手なんじゃないかと思いました。具体的には、

  • エンジニアになる作戦
    • まずはサーバーサイド関連のインターン or 勉強会に参加する
    • 出来る人におすすめの技術書を聞いて読む
    • 月1くらいでアウトプットをする
  • ディレクターになる作戦
    • 今流行ってるものや面白いものについて考えてみる
    • イデアをメモにストックしておく(している人が多い)
    • 色んな人と話す&遊ぶ

というような感じです。今まで私がしてきたことに後悔がある訳ではありませんが、就職活動を始めてから社会人の方とお話することが増えて、"もう少し上手くやってれば…" とか、"あの人みたいな選択をしていたら…" とか、思うことが凄く多くあります。でもまだ19才なので! もっと大人になってから後悔しないように、今から出来ることを増やしていきます。


以上が面白法人カヤックさんとブレスト面談をさせていただいた記録です。ちゃんと書けたかな… ブレスト面談は勿論、インターン中にお世話になった皆さん、話を聞かせて下さった皆さん、本当にありがとうございました。いつか私も同じ目線で話せるようになればいいなぁ~

もしこの記事をご覧になった優しい方がいらっしゃれば、ぜひご指摘やアドバイスをお待ちしています! おすすめの本やインターンの情報もあれば、そっと教えて頂けると凄く嬉しいです! どうぞよろしくお願いします。

言語処理100本ノック【第2章 後編】

課題研究で言語処理100本ノック 2015に取り組んでいます。前回の続きです。使用言語はPython3です。今回は第2章後半の5つを説明します。

15. 末尾のN行を出力

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

ソースコード

import sys

args = sys.argv

f = open("hightemp.txt", "r")
lines = f.readlines()

for i in range(len(lines) - int(args[1]), len(lines)):
  print(lines[i], end = "")

f.close()

前回説明した、14. 先頭からN行を出力 と全体的な構成がほとんど同じです。このプログラムでは末尾のN行を出力するので、range()内の値を工夫しました。range()は指定して引数に従ってリストオブジェクトを返す関数です。range()の第一引数に、テキストファイルの全体の行数 - 自然数Nを指定することで、末尾のN行の先頭の行番号から末尾までの行を出力します。

UNIXコマンド

tail -n N hightemp.txt

tailはテキストファイルの先頭のN行またはNバイトを抜き出すコマンドです。-nオプションを指定すると、指定したテキストファイルの先頭からN行を抜き出します。(headと同じ使い方!)

実行結果

N = 3 の時

山梨県    大月  39.9    1990-07-19
山形県   鶴岡  39.9    1978-08-03
愛知県   名古屋   39.9    1942-08-02

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

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

ソースコード

import sys

args = sys.argv

f = open("hightemp.txt", "r")
lines = f.readlines()

n = 0

for i in range(int(args[1])):
  a = open("16out/out" + str(i) + ".txt", "w")
  for j in range((len(lines) + i) // int(args[1])):
    a.write(lines[n])
    n += 1
  a.close()

f.close()

この問題は、少しあやふやなところがあって、研究室内でもちょっとした議論になりました。議論の詳細はもうちょっと後に書きます。まずはプログラムの説明からです。

このプログラム上ではA行のテキストをN個のファイルにA / N 行ずつ均等に分けます。例えば、5行のテキストを3個のファイルに分けると下の図の様になります。

f:id:f_respectful12:20180304233612p:plain

この例で考えると、この問題のポイントは分けたあとのファイルが2行だったり1行だったりすることです。そこで分けた後の何番目のファイルに何行が対応するかを計算式で与えていきます。初めのfor分で何番目のファイルかを i に記録し、2つ目のfor文で ( ( 元のファイルの行数 len(lines) + 分けたあとのファイルの順番 i ) // 分けたあとのファイルの数 N ) を計算して、何行が対応するかを求めました。ここで//演算子は、切り捨て除算を表します。

実行結果

N = 7の場合(out0.txtからout6.txtまで生成される)

out0.txt

高知県    江川崎   41  2013-08-12
埼玉県   熊谷  40.9    2007-08-16
岐阜県   多治見   40.9    2007-08-16
山形県   山形  40.8    1933-07-25

前述の通り、この問題には少しあやふやなところがあります。問題の解説文を確認すると、

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

とあります。N分割というのは一般的に全体をN個に分けることを言うのでは無いかと思っていました。しかし、この問題で言及されているsplitは、全体をN個ずつ分けるコマンドです。どっちが問題の本質なのか…もっと凄いやり方があるのか… よければ是非、ご指摘&ご意見頂けると嬉しいです。

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

1列目の文字列の種類(異なる文字列の集合)を求めよ.確認にはsort, uniqコマンドを用いよ.

ソースコード

f = open("hightemp.txt", "r")
lines = f.readlines()

a = set([])

for i in range(len(lines)):
  line = lines[i].split("\t")
  a.add(line[0])

print(a)

f.close()

問題で与えられたファイルhightemp.txtの1列目は、都道府県の名前です。2列目は重複しない都市の名前ですが、1列目は重複する場合があります。この問題では、その重複を考えず、1列目で得られる都道府県の種類を答えます。

はじめに、空のリストをset()でセットに変換します。セットは集合を表すデータ型なので、重複した値を持ちません。この性質を利用して、split("\t")で分割した行の1列目をadd()メソッドでセットに全て追加しました。

UNIXコマンド

cat hightemp.txt | awk '{print $1}' | sort | uniq

|(パイプ)は複数のコマンドを組み合わせる為に用いられます。|の左側の処理の出力を右側の処理に渡すのが主な働きです。ここでは、|を使ってcat awk sort uniq の4つのコマンドを組み合わせています。4つのコマンドそれぞれの働きは、下の表で説明します。

コマンド名 コマンドの働き
cat 指定したファイルを全て出力する
awk テキストのパターン処理を行う('{print $1}' は1列目を抜き出す)
sort 行ごとに辞書順に並び替える
uniq 連続して重複した行を1つにまとめる

実行結果

{'愛知県', '山梨県', '群馬県', '静岡県', '岐阜県', '和歌山県', '大阪府', '千葉県', '愛媛県', '高知県', '埼玉県', '山形県'}

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

各行を3コラム目の数値の逆順で整列せよ(注意: 各行の内容は変更せずに並び替えよ).確認にはsortコマンドを用いよ(この問題はコマンドで実行した時の結果と合わなくてもよい).

ソースコード

import sys

args = sys.argv

f = open("hightemp.txt", "r")
lines = f.readlines()

a = {}

for i in range(len(lines)):
  line = lines[i].split("\t")
  a[lines[i]] = line[2]

b = sorted(a.items(), key=lambda x: x[1])

for i in reversed(b):
  print(i[0], end = "")

f.close()

このプログラムでは各行の内容とその行の3カラム目の内容を辞書で対応させて、3カラム目の内容でソートしました。辞書のソートにはsorted()関数とlambda記法を用いました。 sorted( 辞書名.items(), key = lambda x: x[1]) とすることで、[ ( 辞書のキー1, 辞書の値1 ), ( 辞書のキー2, 辞書の値2 ) ... ] といった、タプルのリストが返ってきます。 またreversed()はリストの順番を反転させる関数で、これによって降順のソートに並び替えられます。

UNIXコマンド




ごめんなさい。まだ出来てません。しばしお待ちください。

実行結果

高知県    江川崎   41  2013-08-12
岐阜県   多治見   40.9    2007-08-16
埼玉県   熊谷  40.9    2007-08-16
(中略)
埼玉県   鳩山  39.9    1997-07-05
千葉県   茂原  39.9    2013-08-11

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

各行の1列目の文字列の出現頻度を求め,その高い順に並べて表示せよ.確認にはcut, uniq, sortコマンドを用いよ.

ソースコード

import sys

args = sys.argv

f = open("hightemp.txt", "r")
lines = f.readlines()

a = {}

for i in range(len(lines)):
  line = lines[i].split("\t")
  a[line[0]] = a.get(line[0], 0) + 1

b = sorted(a.items(), key=lambda x: x[1])

for i in reversed(b):
  print(i)

f.close()

get()は辞書からキーを指定して値を取り出すメソッドです。get()メソッドの特徴はキーが登録されていない時にデフォルト値を返すという点で、デフォルト値はget()メソッドの第2引数に指定します。このプログラムでは、辞書に都道府県名と出現頻度を対応させました。都道府県名 (キー) に対応する出現頻度 () が無ければ0を返し、1ずつ足すことで、都道府県ごとの出現頻度を保持します。

UNIXコード

cut -f1 hightemp.txt | sort | uniq -c | sort --reverse

実行結果

('群馬県', 3)
('山梨県', 3)
('山形県', 3)
(中略)
('和歌山県', 1)
('高知県', 1)

実は今、横浜へインターンに来ています。自然言語処理は全く関係のない業界の会社です笑 でも、どんなことでも、面白いことが出来るのは凄く幸せだと思ってます。まだまだ頑張るぞー!

言語処理100本ノック【第2章 前編】

課題研究で言語処理100本ノック 2015に取り組んでいます。前回の続きです。使用言語はPython3です。今回からは第2章 UNIXコマンドの基礎 です。まずは第2章前半の5つを説明します。

第2章の概要

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

10. 行数のカウント

行数をカウントせよ.確認にはwcコマンドを用いよ.

ソースコード

f = open("hightemp.txt", "r")
print(len(f.readlines()))
f.close()

open()はファイルオブジェクトを取得する関数です。第1引数には対象のファイル名、第2引数にはオブジェクトを取得する際のモードを指定します。このプログラムではファイルを読み込ん(read)で操作するので、 r モードを指定しました。実はopen()関数の第2引数はデフォルトで r を指定するので、ファイルを読み込みたい時は、モードを省略することもできます。

readlines()open()関数のメソッドで、読み込んだファイルをリストにして返します。リストの要素はファイルの各行です。例えば、

みかんは美味しい
りんごも美味しい
いちごも美味しい

というテキストファイルに対して、readlines()メソッドを用いると ["みかんは美味しい", "りんごも美味しい", "いちごも美味しい"] というリストが返ってきます。このリストの長さを数えることで行数をカウントしました。

取得した(開いた)ファイルオブジェクトは、close()メソッドで閉じ、メモリを解放します。(C言語と一緒)

UNIXコマンド

wc -l hightemp.txt

wcはファイルのバイト数、行数、文字数または単語数をカウントするコマンドです。-lをオプションに指定すると、ファイルの行数を数えることが出来ます。

実行結果

24

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

タブ1文字につきスペース1文字に置換せよ.確認にはsedコマンド,trコマンド,もしくはexpandコマンドを用いよ.

ソースコード

f = open("hightemp.txt", "r")
print(f.read().replace("\t", " "), end="")
f.close()

read()open()関数のメソッドで、読み込んだファイルの全てのデータを文字列型で読み込みます。replace()は文字列を置換する関数で、第1引数に指定した文字を第2引数に指定した文字に置き換えます。

read()メソッドで読み込んだデータをそのまま出力すると、print()関数の都合上末尾に 改行コードが付与されてしまいます。ここでは、endオプションを用いて末尾で改行されないようにしました。endオプションは指定した文字列を出力の末尾に付与します。(デフォルトは \n

UNIXコマンド

expand -t 1 hightemp.txt

私はUNIXコマンドにはexpandを使いました。expandはタブをスペースに変換するコマンドです。-tオプションでタブ幅( = スペースに変換すると何文字分の幅を取っているか)を指定しています。

実行結果

高知県 江川崎 41 2013-08-12
埼玉県 熊谷 40.9 2007-08-16
岐阜県 多治見 40.9 2007-08-16
(中略)
山形県 鶴岡 39.9 1978-08-03
愛知県 名古屋 39.9 1942-08-02

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

ソースコード

f = open("hightemp.txt", "r")
a = open("col1.txt", "w")
b = open("col2.txt", "w")

lines = f.readlines()

for line in lines:
  datas = line.split()
  a.writelines(datas[0] + "\n")
  b.writelines(datas[1] + "\n")

f.close()
a.close()
b.close()

結果を出力するファイルである col1.txt と col2.txt は、w モードで取得します。wモードを用いると、取得したファイルオブジェクトに対して書き込み(write)を行えます。ファイルの行ごとのデータを持つ lines リストに対してfor文を実行し、得た文字列 line に対して列のデータを取得しました。列は縦向きに並んでいるデータです。

writelines()open()関数のメソッドで、ファイルオブジェクトに対して1行ずつ書き込みを行います。print()関数と違ってデフォルトで末尾に改行コードが付与されないので、改行する場合は別に \n を追記する必要があります。

UNIXコマンド

cut -f 1 hightemp.txt > col1.txt #1列目について
cut -f 2 hightemp.txt > col2.txt #2列目について

cutは文字列から所定の位置にある文字列を抜き出すコマンドです。-fオプションを用いると、指定した順番のフィールドだけを切り出す事ができます。フィールドとは特定の区切り文字列で区切られた文字列のことです。例えば、

りんご みかん いちご

という文字列に対して、"りんご"、”みかん"、”いちご” といったフィールドが順番に得られます。cutコマンドで得た出力を > を用いて別のファイルに保存しました。

実行結果

col1.txt

高知県
埼玉県
岐阜県
(中略)
山形県
愛知県

col2.txt

江川崎
熊谷
多治見
(中略)
鶴岡
名古屋

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

12で作ったcol1.txtとcol2.txtを結合し,元のファイルの1列目と2列目をタブ区切りで並べたテキストファイルを作成せよ.確認にはpasteコマンドを用いよ.

ソースコード

a = open("col1.txt", "r")
b = open("col2.txt", "r")
f = open("col.txt", "w")

col1 = a.readlines()
col2 = b.readlines()

for data1, data2 in zip(col1, col2):
  f.writelines(data1.replace("\n", "") + "\t" + data2);

a.close()
b.close()
f.close()

col1.txt と col2.txt の結果をそれぞれ行ごとのリストで取得します。zip()は、複数のシーケンス型オブジェクトをまとめてループさせる為の関数です。このプログラムでは、2つのリストに対して先頭から同じ順番に要素を得ています。col1 から得られるデータには、不要な \n がついているので、replace()を使って除去しました。

UNIXコマンド

paste col1.txt col2.txt > col.txt

pasteは複数のファイルを行単位で連結するコマンドです。-dオプションを指定しない場合、タブ文字で連結されます。

実行結果

高知県    江川崎
埼玉県   熊谷
岐阜県   多治見
(中略)
山形県   鶴岡
愛知県   名古屋

14. 先頭からN行を出力

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

ソースコード

import sys

args = sys.argv

f = open("hightemp.txt", "r")
lines = f.readlines()

for i in range(int(args[1])):
  print(lines[i], end = "")

f.close()

コマンドライン引数をプログラムで使用するために、sysモジュールをインポートしました。sys.argvコマンドライン引数をリストで返す関数で、args[0]では実行したプログラム名が得られます。例えば、

python 13.py 3

といった風にプログラムを実行すると、args[0] = "13.py"args[1] = "3" が取得できます。このプログラムでは args[1]自然数N(ただし文字列型)が得られることが分かっているので、int()関数で int型に変換して、N個分の行を出力しました。

UNIXコマンド

head -n N hightemp.txt

headはテキストファイルの先頭のN行またはNバイトを抜き出すコマンドです。-nオプションを指定すると、指定したテキストファイルの先頭からN行を抜き出します。

実行結果

N = 3 の時

高知県    江川崎   41  2013-08-12
埼玉県   熊谷  40.9    2007-08-16
岐阜県   多治見   40.9    2007-08-16

実はこの記事は1度途中まで書いていたのですが、途中でWifiが切れてデータが吹っ飛んでしまったので、泣く泣く最初から書き直しました。こまめに保存しとくのってめっちゃ大事や…

言語処理100本ノック【第1章 後編】

課題研究で言語処理100本ノック 2015に取り組んでいます。前回の続きです。使用言語はPython3です。今回は第1章後半の5つを説明します。

作ったプログラムはGitHubに公開しています。指摘等あれば是非よろしくお願いします!

05. n-gram

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

ソースコード

a = "I am an NLPer"

word = a.split()
char = list(a)

def ngram(target, n):
  result = []
  if len(target) >= n:
    for i in range(len(target) - n + 1):
      result.append("".join(target[i:i + n]))
  return result  

print(ngram(word, 2))
print(ngram(char, 2))

n-gramというのは、ある文字列に対してn文字(もしくはn単語)ごとに切り出す操作のことです。n = 1 の時 uni-gram、n = 2 の時 bi-gram、n = 3 の時 tri-gramという名前が付いています。例えば "私は学生です。" という文字列に対して 文字bi-gramを考えると以下のように文字列が切り出されます。

私は / は学 / 学生 / 生で / です / す。

これは文字列中の単語に関しても、同じように考えることができます。

このプログラムでは与えられた文字列に対して、文字や単語を要素とするリストを作成した後、自作のngram()を用いてbi-gramの操作を行いました。ngram()は引数として与えられたリストに対して必要なn要素をスライスで切り出します。切り出された要素はjoin()を用いて文字列に戻し、結果を保有するリストに順次追加しました。

ngram()を用いたbi-gramは、文字と単語について別々に行います。

実行結果

['Iam', 'aman', 'anNLPer']
['I ', ' a', 'am', 'm ', ' a', 'an', 'n ', ' N', 'NL', 'LP', 'Pe', 'er']

06. 集合

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

ソースコード

a = "paraparaparadise"
b = "paragraph"

charA = list(a)
charB = list(b)

def ngram(target, n):
  result = []
  if len(target) >= n:
    for i in range(len(target) - n + 1):
      result.append("".join(target[i:i + n]))
  return result  

setA = set(ngram(charA, 2))
setB = set(ngram(charB, 2))

print(setA.union(setB))
print(setA.intersection(setB))
print(setA.difference(setB))
print(setB.difference(setA))

print("se" in setA)
print("se" in setB)

このプログラムでは文字列の文字bi-gramを求めたあと、セットを用いた集合演算を行いました。セットとは集合を表すデータ型です。リストのように複数の要素を持つことができますが、集合を表すので、要素に順番は無く重複する値を持ちません。set()を用いることでリストをセットに変換することが出来ます。

集合演算に用いたメソッドは以下の表の通りです。

メソッド名 構文 構文の意味
union() A.union(B) 集合Aと集合Bの和集合
intersection() A.union(B) 集合Aと集合Bの積集合
difference() A.difference(B) 集合Aから集合Bを引いた差集合

集合にある要素が含まれるかどうかを調べる為には、in演算子を用いました。in演算子は右側に示すデータの集まりに、左側に示す要素が含まれているとき True を返します。

実行結果

{'ar', 'ag', 'ad', 'di', 'is', 'ph', 'ra', 'se', 'gr', 'pa', 'ap'}
{'ra', 'pa', 'ap', 'ar'}
{'ad', 'se', 'di', 'is'}
{'gr', 'ph', 'ag'}
True
False

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

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

ソースコード

def template(time, factor, value):
  print("{0}時の{1}は{2}".format(time, factor, value))
  
template(12, "気温", 22.4)

format()は文字列の内部の変数に対して後から値を埋め込むことができる関数です。Pythonでは+演算子を使って直接文字列や変数を連結させることもできますが、format()は複数の変数を文字列の後ろでまとめられるのが特徴です。(C言語printf()っぽい使い方ができます)format()の引数は左から順番に文字列内の{0}、{1}、{2}…に対応します。

実行結果

12時の気温は22.4

08. 暗号文

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

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

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

ソースコード

a = "Now I need a drink, alcoholic of course, after the heavy lectures involving quantum mechanics."

def cipher(text):
  encr = ""
  for char in text:
    if char.islower():
      encr = "".join([encr, chr(219 - ord(char))])
    else:
      encr = "".join([encr, char])
  return encr

print(a)
print(cipher(a))
print(cipher(cipher(a)))

コンピュータ上に表される全ての文字は、Unicodeと呼ばれる文字の規格上で整数に変換することができ、この文字を変換した後の整数を文字コードといいます。

このプログラム内で用いられている ord()は文字を文字コードに変換する関数で、chr()文字コードを文字に変換する関数です。また、ある文字に対して小文字であるかどうかの判別をする際にはislower()を用いました。islower()は左側に接続された文字が小文字であれば True を返します。

実行結果

Now I need a drink, alcoholic of course, after the heavy lectures involving quantum mechanics.
Nld I mvvw z wirmp, zoxlslorx lu xlfihv, zugvi gsv svzeb ovxgfivh rmeloermt jfzmgfn nvxszmrxh.
Now I need a drink, alcoholic of course, after the heavy lectures involving quantum mechanics.

少しややこしいですが、この問題で求められているのは、暗号化対象の文字を文字コードに変換した後、適当な整数を足し引きすることで架空の文字コードを生成し、これに対応する文字を暗号化された文字とすることです。暗号化された文字に対してこの逆向きの処理を行うと復号化することができます。

f:id:f_respectful12:20180221170251p:plain

09. Typoglycemia

スペースで区切られた単語列に対して,各単語の先頭と末尾の文字は残し,それ以外の文字の順序をランダムに並び替えるプログラムを作成せよ.ただし,長さが4以下の単語は並び替えないこととする.適当な英語の文を与え,その実行結果を確認せよ.

ソースコード

import random

a = "I couldn't believe that I could actually understand what I was reading : the phenomenal power of the human mind ."

a = a.split(" ")
chimera = []

for word in a:
  if 4 < len(word):
    chars = list(word[1:-1])
    random.shuffle(chars)
    chimera.append(word[0] + "".join(chars) + word[-1])
  else:
    chimera.append(word)

print(" ".join(chimera))

文字の順序をランダムに並び替える為に、randomモジュールのrandom.shuffle()を使いました。 random.shuffle()は与えられたリストの中身をランダムに並び替えることができます。

このプログラムでは先頭と末尾以外の文字は、要素の2番目から要素の後ろから2番目までをスライスを使うことで抜き出しました。要素の後ろから2番目を表すスライスは負の数を使って [:-1] で表現することができます。先頭と末尾以外の文字をランダムに並び替えたら、+演算子を使って先頭と末尾を加えました。

実行結果

I cul'dnot bveeile that I cluod actually undntaesrd what I was rinedag : the panohmeenl pewor of the hmuan mind .

ここまでが第1章で自然言語処理100本ノックの準備運動です。 私は 08. 暗号文 で詰まったのでちょっとだけ説明多めにしてます。 次も書くぞ!

言語処理100本ノック【第1章 前編】

課題研究で言語処理100本ノック 2015に取り組んでいます。使用言語はPython3です。文章を書く練習も兼ねてちょっとずつ記録を残していこうと思います。初めは第1章前半の5つです。

作ったプログラムはGitHubに公開しています。指摘等あれば是非よろしくお願いします!

00. 文字列の逆順

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

ソースコード

f = reversed(list("stressed"))
a = ''.join(f)
print(a)

list()は、シーケンス型のオブジェクト(順序のある要素の集まり)からリストを作成する関数です。文字列も左から順番に読む要素(アルファベット)の集まりだと考えられるので、list()を用いるとリストとして扱うことが出来ます。reversed()はリストを逆順にする関数です。

これをjoin()を使って文字列に戻し、出力します。

実行結果

desserts

01. 「パタトクカシー」

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

ソースコード

a = "パタトクカシー"[::2]
print(a)

今回はスライスを使いました。スライスとは、シーケンス型のオブジェクトから欲しい要素を抜き出す書き方です。オブジェクトに対して[始まりの位置 : 終わりの位置 : ステップ]を右側に書くことで使えます。始まりの位置を省略すると0、終わりの位置を省略するとオブジェクトの末尾が参照されます。このプログラムでは、始まりの位置 = 0、終わりの位置 = 末尾、ステップ = 2なので、文字列の左端から最後まで順番に1文字飛ばしで要素が抜き出されます。

抜き出した要素はそのまま文字列として合体して返ってくるので、そのまま出力します。

実行結果

パトカー

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

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

ソースコード

a = list("パトカー")
b = list("タクシー")
c = []

for i, j in zip(a, b):
  c += i + j

c = "".join(c)

print(c)

"パトカー"と"タクシー"をlist()でリストに変換した後、2つのリストの先頭から順番に1つずつ要素を呼び出して合体させていきます。zip()は、2つ以上のリストの各要素をタプルにまとめて返す関数です。zip()を用いるとfor文の最初のループで、i = "パ"、j = "タ"が返ってきます。文字列同士は+演算子を使って連結し、+=演算子を使って別に用意したリストに要素として追加します。

これをjoin()を使って文字列に戻し、出力します。

実行結果

パタトクカシーー

03. 円周率

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

ソースコード

import re

a = "Now I need a drink, alcoholic of course, after the heavy lectures involving quantum mechanics."

a = re.sub(r'[\.,]+', "", a)
a = a.split()
result = []

for w in a:
  result.append(len(w))

print(result)

この英文には解答に必要のない ", " とか ". " が含まれているので、reモジュールをインポートし正規表現を使って取り除きました。正規表現とは文字列のパターンを照合する表現のことです。例えば[\.,]だと、", " か ". " のどちらかであることを表しています。re.sub()正規表現を使って文字列を置換する関数です。re.sub(正規表現, 置換後の文字列, 置換対象) と記述して使います。このプログラムでは、英文に含まれる ", " や ". " を何も含まない文字列 "" に置換しました。split()は文字列を区切る関数です。引数を省略すると文字列に対してスペース区切りしたものを リストにして返してくれます。

re.sub()split()を使って得られたリストから順番に単語を取り出し、len()を使って文字数を得ます。この文字数をあらかじめ用意した別のリストに要素として追加することで文字数を先頭から出現順に並べたリストが完成します。

実行結果

[3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5, 8, 9, 7, 9]

ちなみに得られたリストには、円周率 3.141592... の数字を順番に並べたものが格納されています。

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文字を取り出し,取り出した文字列から単語の位置(先頭から何番目の単語か)への連想配列(辞書型もしくはマップ型)を作成せよ.

ソースコード

import re

a = "Hi He Lied Because Boron Could Not Oxidize Fluorine. New Nations Might Also Sign Peace Security Clause. Arthur King Can."

pull = [1, 5, 6, 7, 8, 9, 15, 16, 19]

a = re.sub(r'[\.,]+', "", a)
a = a.split()
result = {}

for i in range(len(a)):
  if i + 1 in pull:
    result[i + 1] = a[i][:1]
  else:
    result[i + 1] = a[i][:2]

print(result)

range()は連番のリストを作る関数です。range()を使って得たインデックスがリストpullに含まれるかを判別して、取り出す文字数を決定します。先頭文字の取得に始まりの位置 = 0、終わりの位置 = 1または2、ステップ = 1のスライスを使っています。

取得したインデックスと先頭文字を格納する為の連想配列には辞書を使用しました。辞書名[辞書のkey] = 辞書の値で辞書にデータを登録出来ます。

実行結果

{1: 'H', 2: 'He', 3: 'Li', 4: 'Be', 5: 'B', 6: 'C', 7: 'N', 8: 'O', 9: 'F', 10: 'Ne', 11: 'Na', 12: 'Mi', 13: 'Al', 14: 'Si', 15: 'P', 16: 'S', 17: 'Cl', 18: 'Ar', 19: 'K', 20: 'Ca'}

もちろん完全無欠のコードじゃないです。 指摘頂いた所も追記していければと思います!