見出し画像

Retro-gaming and so on

余計なお世話を焼いてみる(笑)

また面白い事を星田さんがやっている。

上手く動いたようで良かった。
ただ、本文中に色んな疑問が書かれてたので、それに勝手に答えてみようと思う。
まぁ、余計なお世話なんだけどな(笑)。

ちなみに、扱ってる文章は実は歌詞ではなく、英語版Wikipediaにおける、マンガ「ベルセルク」の解説から抜き取ってきたみたい。
上手い手だよなぁ(笑)。感心してた。

1. 折角ですのでアドバイス通り今回は辞書形式を使ってみます。これがダルいw

うん、まぁ(笑)。慣れないとアレかもね。
ただ、このテの「プログラムで再利用するのがほぼ決まってる」データ型は本体と分離しておいた方が良い。
ある種セオリーかな。
と言うのも、アッチコッチにデータがとっちらかってると修正する時にメンド臭いわけ。
だからデータはデータで纏めておいた方が良い。それは別に1個にする必要はないし、場合によっては2個、3個使っていっても良い。

今回のポイントってのは最小限のHTMLが次のようになってる、って前提なのね。

<!doctype html>
<html>
<head><title>なんかのタイトル</title></head>
<body>
<!-- コンテンツを配置 -->
</body>
</html>


もちろん慣れればもっと凝ったHTMLを書いても良いけど、取り敢えずややこしさを避けて雛形としてはこれで良いでしょ。
んで、上のコンテンツにあたるのがdivタグなわけね。

<div>なんとやら</div>

まず、カラー化云々、って前にこれを押さえておく。
そして、OPMLなんかもそうだし、HTMLなんかもそうなんだけど、基本、タグってのはペアで一個なわけ。上で言うと<div>があれば必ず</div>をどっかで引き連れていて、それが無いとHTMLに違反してるわけ。
と言うか、それがマークアップ言語と言う仕組みなのね。本文は必ず何かに挟まれている
と言うことはさ。プログラムで扱う際に<div></div>をバラす必要がないわけ。メンド臭いじゃない?
つまり、データとしては、前見たように、穴あきを使って

<div>{}</div>

と纏めてしまった方が良い。その方が恐らく見通しは良くなるはず。
ってなわけで、結局これも(今のトコ)3パートしかなく、

table = {"head": "<!doctype html><html><head><title>{}</title></head><body>",
   "div" : "<div>{}</div>",
   "tail": "</body></html>"}

で取り敢えずは済む。
タイトルも穴あき、divも穴あきにしておいて、後でformatで埋める。
ここはあとでちょっと改良するけど、取り敢えずはこの3つのデータがあれば、HTML文書は「作れる」って事になる。

2. はっきり言って関数を使うのも初めてなので、どういう時に関数を使うべきなのかがはっきりしないんですよね。何でも関数にすれば良いのかな?

うん、極論何でも関数にしてO.K.です。
ええとね、これは文法の問題じゃなくって、あくまでUNIX的書法な考え方なんですが。
セオリー的には次の二つが良く言われます。

  1. 1つの関数は1つの機能に絞る事。
  2. 関数のテキストエディタ上の行数の上限は12行程度にしておくこと。
かな。まぁ、なかなか上手く行かないんだけど、1番に関してはある程度の実例は後述しましょう。
2番に関して。何故に12行?とか言うかもしれんけど、根拠はないです(笑)。ただ、要するに、関数を読む時、エディタをスクロールさせずに1画面で全体を把握出来る量にしとけ、って事ですね。スクロールさせながら読むとメンド臭いわけじゃん(笑)?
まぁ、この辺言語に拠るし、例えばC言語はオマジナイが多いし、Rubyはendに1行取られる、とか、そういう差がどうしても生じてしまうんですが。
Pythonは比較的「縦に長く」成りづらいプログラムが書けるんで、12行辺りは結構妥当かな、とか言う気はします。逆に12行超えてくると、1番に反して「一つの関数に機能を詰め込み過ぎてる」サインになるわけよ。

んでね、星田さんが書いた仕様が良く出来てるんですよ。

行ごとにリストに読み込んで→スペースで単語ごとに別のリストに入れて→最後の単語だけバラバラに分解して→その中から母音を抜き出して条件分岐でタグをつけて・・

プログラム書けない人ってまずこの段階がダメなわけ(笑)。何をどーしてあーする、って言う、何だろ、ロードマップが形成されない。
んで、

リストを分解していったのを逆の流れでつないで行くのだが・・・


うん、それも全面的に正しい。
でもね、手順を全部「いきなりやろうとするから」混乱するんです。
こういう時が関数の出番。「ちいちゃな事を一つやる」だけの関数をまず作って、次はそれ使って「もうちょっと大きな事やって」と「組み立てて」いく。
まず最初にやるこたぁ、「逆順で」ってぇんで次をピックアップします。

その中から母音を抜き出して条件分岐でタグをつけ

まずはね、母音の文字に反応してタグで挟むコードを書く。いきなり文字列対象として考えない。メンド臭いから(笑)。
その前にちょっとtableをイジります。

table = {"head": "<!doctype html><html><head><title>{}</title></head><body>",
    "div" : "<div>{}</div>",
    "tail": "</body></html>",
    "a": "<span style=\"background-color:rgb(255, 147, 128);\">{}</span>",
    "e": "<span style=\"background-color:rgb(153, 194, 255);\">{}</span>",
    "i": "<span style=\"background-color:rgb(255, 255, 78);\">{}</span>",
    "o": "<span style=\"background-color:rgb(145, 242, 97);\">{}</span>",
    "u": "<span style=\"background-color:rgb(255, 128, 200);\">{}</span>"}

さっきのtableにa、e、i、o、uそれぞれに対応したカラー指定のspanタグを設定する。
もちろん、専用にデータテーブルをまた別に作ってもいいんだけど、メンド臭いんで、纏めました。「メンド臭い」って良いキーワードだな(笑)。
んでね、こうしておくと何が良いのか。

条件分岐でタグをつけ

そう、条件分岐する必要がなくなるの(笑)。
だって、

>>> table["u"]
'<span style="background-color:rgb(255, 128, 200);">{}</span>'
>>>

でしょ?辞書から引っ張ってくれば別に条件分岐する必要がない。
ここでTipsその1。

データ構造を伴いながら条件分岐をしなければいけないような場合、辞書型を使って条件分岐を消してしまえ。

ifelif〜elseとかメンド臭い。何らの条件分岐せなアカン、とか言う場合、まずは辞書型を使用する事を検討した方が良い、って事です。
つまり、基本的には

def rgb(s):
 return table[s.lower()].format(s)

で母音文字はマークアップで修飾出来る、って事を意味する。
関数が1行で済む、とかサイコーだね(笑)!
んでこの嘆きに対して。

色々とやってみて == "a" or "A" の書き方が問題だった模様。そりゃそうだw もう面倒なので小文字だけにする

また一つTips。その2。

文字の大文字化、ないしは小文字化を使え

つまり、情報として渡ってきた文字sを辞書型tableのキーに合わせて無理矢理小文字化とかしちまえばいいわけだ。
それがこの部分。

s.lower()

そうすれば、sが大文字だろうが小文字だろうが関係がなくなる。

>>> rgb("e")
'<span style="background-color:rgb(153, 194, 255);">e</span>'
>>> rgb("E")
'<span style="background-color:rgb(153, 194, 255);">E</span>'
>>>

でしょ?
でもこの関数、このままだと問題があるのね。
それは辞書tableに存在しないキーが渡された場合。

>>> rgb("p")
Traceback (most recent call last):
File "<pyshell#23>", line 1, in <module>
rgb("p")
File "<pyshell#22>", line 2, in rgb
return table[s.lower()].format(s)
KeyError: 'p'
>>>

関数rgbに子音pを与えるとエラーを返してくるわけよ。「そんなキーはtableに存在しません」と。例外を投げてくるKeyErrorと言う例外だ。
逆に言うと、この例外を掴まえたら問題なく関数rgbが完成する。

def rgb(s):
 try:
  return table[s.lower()].format(s)
 except KeyError:
  return s

キーが無いときゃ文字をそのまま返せばいいんだから、これでオシマイ。
これで「文字が母音だったらspanタグで挟み、そうじゃなかったらそのまま文字を返す」と言う簡単な関数が設計出来た。

3. スペースで単語ごとに別のリストに入れて→最後の単語だけバラバラに分解する

これはひとまとめにしてしまおう。
というのも、

  1. 文字列を得て文字列を返しそうだから。
  2. 文字列の最後の単語だけ母音に対するカラーリングタグを施すから。
結局「ちょっとだけ文字列を処理する」だけなのよね。
上で書いた関数rgbは文字に対する関数、今度はちょっと大きな文字列に対する関数だ。

んで、例えば今作る関数は以下のような文字列を受け取るわけだ。

>>> s = "Set in a medieval Europe-inspired dark fantasy world"
>>>

そして、「スペースで単語ごとに別のリストに入れて」ってのはPythonでは次のような意味になる。

>>> baralist = s.split(" ")
>>> baralist
['Set', 'in', 'a', 'medieval', 'Europe-inspired', 'dark', 'fantasy', 'world']
>>>

ここで文字列のsplitメソッドを使う。引数にスペース(" ")を与えると、そこで文字列をバラバラにする。って事はまさしく要求仕様なわけだね。
そして文字列の最後の単語だけ色替えをすればいいんだけど、その状況に持ち込む為にはまず、上のbaralistをスライスせなアカンと言う事。場所は、リストのケツ方面から先頭に向かって分割するんで、baralistの最終要素は-1番目、そしてそれ「以前」の部分も-1から前、って考えれば良い。

☆baralistを先頭方面のbaralist[:-1]と末尾のbaralist[-1]に分割する

んでカラー情報を付け加えなければならないのは末尾のbaralist[-1]なわけだな。
さて、ここでいよいよリスト内包表記の出番だ。

例えば今ここでworldと言う文字列がある。これはbaralist[-1]の中身だね、一応。
幸いな事にリスト内包表記は「走査対象」はシーケンス、あるいはイテラブルを受け取る事が出来る。文字列なんかもシーケンス/イテラブルだ。
だから例としては、中途半端なんだけど次の部分解だけは書き下す事が可能となる。

[for i in 'world']

もうこれだけで「文字列worldの先頭から一文字一文字iとして引っこ抜いてきて」と言う意味になる。
残るはiと言う変数、中身は文字列worldから引っこ抜いてきた一文字、を「どう加工するか」だけ。
答えは既にある。上で作った関数rgbがその答えだ。

>>> [rgb(i) for i in 'world']
['w', '<span style="background-color:rgb(145, 242, 97);">o</span>', 'r', 'l', 'd']
>>>

リスト内包表記だから返り値はリストになってる。
こいつを文字列に直すには一気にこんなカンジで書いておく。

>>> "".join([rgb(i) for i in 'world'])
'w<span style="background-color:rgb(145, 242, 97);">o</span>rld'
>>>

joinとリスト内包表記の合わせ技だ。これでbaralistのケツ対策はなんとかなりそうだ、って事が分かる。
そして、最後に文字列を返す以上、

☆baralist[:-1]を文字列にしたもの + baralist[-1]の母音にカラーリングし、文字列にしたもの

returnすりゃエエのは一目瞭然、である。
これが求める関数の方程式だ。
従って、

def tail2rgb(s):
 baralist = s.split(" ")
 return " ".join(baralist[:-1]) + " " + "".join([rgb(i) for i in baralist[-1]])

joinだらけに見えるけど、たった2行で書けるんだから良い仕事をしてくれてると思う(笑)。
ポイントは、文字列s等はスペースを目印にして分割されてるんで、結果スペースが除去されてる事。
そこで文字列に戻す際、適宣スペースを補いつつ結合せなアカン、と言う事。

さて、動作を確認してみよう。

>>> s = "the story centers on the characters of Guts"
>>> tail2rgb(s)
'the story centers on the characters of G<span style="background-color:rgb(255, 128, 200);">u</span>ts'
>>>

うん、上手い具合に要求仕様は満たしているようだ。

4. 行ごとにリストに読み込む

最後はちと抽象的だね。
でもいいのだ。大枠は大体抽象的なんだ、って相場は決まってる。

ここまで関数を二つ作ってきたんだけど、「細かい部分を操作する」から「ちょっと大きな枠を操作する」と進んできた。
具体的には、

  1. 要求仕様に従って「文字」を弄る関数を得た
  2. 要求仕様に従って「文字列」を弄る関数を得た
今度は「文字列のリストを弄る関数」を得よう、ってなワケだ。大枠も大枠だ。

取り敢えず、「テキストファイルを読み込むと」文字列のリストを得る、って事は分かってるわけだ。readlinesの返り値がそうだから、だ。
そして、幸いな事に、一行一行、つまり、リストの要素要素に何を適用すれば良いのか、ってのは既に分かってる。っつーか作った。関数tail2rgbがそれだ。

つまり、大まかには次の二つの作業が必要だ、と言うことだ。

  1. 各行に関数tail2rgbを適用する。
  2. 各行を<div></div>で包む。
そして大枠のリスト自体はそのままで良い。と言うのも、writelinesを使うならその方が面倒がないから、だ。つまり返り値は「文字列のリスト」のままで良いのだ。
結果、ここで書く関数は「文字列のリストを受け取って文字列のリストを返す」関数である。
オチも完璧だ。

例えば読み込まれる文字列のリストが以下のようだったとしよう。

>>> datalist = ["Set in a medieval Europe-inspired dark fantasy world",
"the story centers on the characters of Guts",
"a lone mercenary, and Griffith"]
>>>

変数datalistは3要素のリストで、それぞれの要素は文字列で1行の内容である。
そうすると、各要素に関数tail2rgbを適用すれば良いので、またもやリスト内包表記の出番である。
大枠は

[ for i in datalist]

で、変数iにはdatalistの要素、つまりこの場合は各行の文字列だな、それが入っていく。
変数iは結果1行の文字列なので、こいつに関数tail2rgbを適用すれば良い。
つまりこうだ。

>>> [tail2rgb(i) for i in datalist]
['Set in a medieval Europe-inspired dark fantasy w<span style="background-color:rgb(145, 242, 97);">o</span>rld', 'the story centers on the characters of G<span style="background-color:rgb(255, 128, 200);">u</span>ts', 'a lone mercenary, and Gr<span style="background-color:rgb(255, 255, 78);">i</span>ff<span style="background-color:rgb(255, 255, 78);">i</span>th']
>>>

上手く行っている。
ついでに<div></div>も適用する。
上の結果を利用するわけだが、結局リスト内包表記のせいでリストが返ってる。
と言う事は返り値のリストを利用してまたもやリスト内包表記を使ってみる、ってのがオツだろう。
「外側における」リスト内包表記の雛形は以下の通りだ。

[ for j in データ]

データ、つまり、先のリスト内包表記の計算結果から順次変数jに文字列がまたもや突っ込まれていくわけだ。
じゃあ、そのjに何を適用するのか、と言うと再び登場、tabledivデータであり、文字列のformatメソッドである。

[table["div"].format(j) for j in データ]

最終的には「リスト内包表記のリスト内包表記」で、合わせてこうなる。

>>> [table["div"].format(j) for j in [tail2rgb(i) for i in datalist]]
['<div>Set in a medieval Europe-inspired dark fantasy w<span style="background-color:rgb(145, 242, 97);">o</span>rld</div>', '<div>the story centers on the characters of G<span style="background-color:rgb(255, 128, 200);">u</span>ts</div>', '<div>a lone mercenary, and Gr<span style="background-color:rgb(255, 255, 78);">i</span>ff<span style="background-color:rgb(255, 255, 78);">i</span>th</div>']
>>>

これで仕組みは出来上がった。で、基本的にはワンライナーで書いたそれをdefで包んでreturnしてやりゃ一丁上がりなんだが、2つばかり追加作業がある。
まず一つ目。星田さんが次のように書いている。

テキストは1行目をタイトルとしてそのまま表示。

うん、って事は、外部から引数としてdatalistを受け取ってきた時、そのdatalistを2分割する必要がある。一行目の部分とそれ以外、だ。

head = datalist[0]
tail = datalist[1:]

と言う事は変数headはまずはtitleタグと共に使うことが出来る。そしてついでに言うと、こいつだけは別口で、divタグのお世話にもならないといけない、と言う事だ。
二つ目。このOPMLの記事にも書かれてるけど、ファイルを開いてreadlinesで読み込む以上、各行は最後に改行文字(\n)が添付されている。こいつを実際はstrip()で削除しなきゃならない。
上のheadも実は改行文字がくっついている。従って、この段階で、次のようにしておけば良い、ってのが分かるだろう。

head = datalist[0].strip()
tail = datalist[1:]

tail側の方は「文字列のリスト」なんで、あとでまとめてstrip()も適用する。具体的には先程のワンライナーを利用してこうしちまえば良いわけだ。

[table["div"].format(j) for j in [tail2rgb(i.strip()) for i in datalist]]

さて、ここまで出来たらあとはHTMLの様式に従ってリスト同士を結合すれば良い。先程の変数headを利用してtitleタグを埋め、table["head"]+tailを操作したもの+table["tail"]を返しちまえば終了、である。

def gorange(datalist):
 head = datalist[0].strip()
 tail = datalist[1:]
 return [table["head"].format(head)] + [table["div"].format(head)] +\
    [table["div"].format(j) for j in [tail2rgb(i.strip()) for i in tail]] +\
    [table["tail"]]

これで完成だ。

ところで、やっぱりこういう

[table["div"].format(j) for j in [tail2rgb(i.strip()) for i in datalist]]

「リスト内包表記を使ったリスト内包表記」とか「やりすぎだ」って感じる向きもあるかもしれない。あるいはワンライナーを狙い過ぎなんじゃないか、書いてる人はワンライナー信者じゃないか、とか。あるいは読みづらいコードを書く事で人々を混乱させるのを楽しんでる、性格の悪い人なんじゃないか、と。
それは誤解です。
んで、確かに一般的にはワンライナーってのは読みづらい。書いてる人は「俺ってこんなん書けるんだぜ?」と言う自己顕示欲「だけの」カタマリに見える事は良くある。確かに良くあるんだ。
ただ上のコードはそういう事は意図してません。
ハッキリ言えば

「関数型言語の流儀だとこうなる」

ってだけなんだな。Python使ってても関数型言語経験者は大体上のように書くし、上のコードを読むのはあまり苦にしない。と言うか、慣れの問題だな。
関数型言語の経験者は、データフローが「内側から外側に流れるように感じる」と言う特性を得るわけだ。
例えば関数適用を()で表現すると、

(<-(<-(<-(データ)->)->)->)

みたいに感じる。一番内側の「データ」から矢印に従って外側へとデータが「流れ出ていってる」と言うような特殊な感覚を持つ。
一方、C言語やその類似の「手続き型言語」とか「オブジェクト指向」の人は「コードの流れ」を一般に以下のようにして捉える。

x = (データ)
y = (x)
z = (y)

代入を駆使して、あくまでテキストの「上から順に」下へと流れるように解釈する。
どっちが良い、たぁ言わない。要するに関数型言語の感覚としては最初の形式を愛好したり、多用したりするケースが多いんで、別にワンライナーにしたくてしてるわけじゃないんだ。
件の「リスト内包表記を利用したリスト内包表記」でも形式は「内側からデータが外側に流れ出る」ようにするのが「自然だ」って、関数型言語経験者はどうしても思っちゃうのね。

[table["div"].format(j) for j in [tail2rgb(i.strip()) for i in datalist]]

分かるかしらん。データフローを感じる(笑)?
いずれにせよ、関数型言語経験者は「返り値と引数の連鎖」ってのを苦にしないの。C言語書く時でさえ・・・あんまねぇけど(笑)、一々変数に代入したりせず、関数の返り値と引数を利用して全部突っ込んじゃう、とかヘーキでやっちゃうね。・・・それが関数型言語経験者の「スタイル」なんだ。
もし、この形式が気に入らない場合、手続き型言語/オブジェクト指向言語的な様式に従って、次のように書いても構わない。

datalist0 = [tail2rgb(i.strip()) for i in datalist]
datalist1 = [table["div"].format(j) for j in datalist0]

そして改めてtable["head"]table["tail"]と結合させてもいいでしょう。そっちが分かりやすい、のなら。
ただ、Pythonは初期の段階からかなり関数型言語に影響を受けて設計されてる、って話は何度かした通り。どうせだったら食わず嫌いせずに「関数型言語的なスタイル」に慣れても良いんじゃないのかな、とは個人的には思っています。

さて、残り、これがある。ちょっとこれを説明しよう。

5. これまたアドバイスをいただいた if__name__ を使ってみることに。解説を読むと、なるほど・・直接実行したときだけ起動するのか・・確かにアプリっぽい挙動になりそうですね

うん。合ってる。
それでまたTipsを挙げよう。

if __name__ == "__main__":を使う場合、単一の小さなプログラム、つまりスクリプトになってる事が多い。その場合、入力ではinputを頼らず、import sysしてsys.argvを使った方がカッコイイ。

これはこういう事だ。
以前、ここでpipの事が書いてあったけど。pipもPythonスクリプトだ。
こいつはPythonインタプリタ上では動かず、端末からパスさえ通ってれば例えば次のように起動する。

pip install –upgrade pip

ここで、pipはプログラムあるいはスクリプト名なのは明らか。じゃあ、残りのinstall –upgrade pipをなんつーか、と言うとこれをコマンドライン引数と呼びます。
pythonだとsys.argvと言うのはこのコマンド全部のリストなのね。["pip", "install", "–upgrade", "pip"]と言うカタチで格納される。言い換えるとsys.argvさえ設定しておけば、わざわざinputを呼ばなくても、コマンドライン引数としてテキストファイル名を端末上から得る事が可能だ、と言う事だ。
つまり、端末上で、例えばberserk.txtgorangeを適用したい場合、

gorange berserk.txt

と打てば、あとは勝手にberserk.htmlが生成される、って事。そしてsys.argvには["gorange", "berserk.txt"]が格納される。
これを実現するためには次のように書く。

if __name__ == '__main__':
 name = sys.argv[1]
 with open(name, 'r') as f:
  with open(name.replace('.txt', '.html'), 'w') as nf:
   nf.writelines(gorange(f.readlines()))

sys.argv["gorange", "berserk.txt"]なんで、nameに引っ張り出す、つまりopenしたいテキスト名にするにはsys[1]を利用しないとならない。リストなんで当然だわな。
本体gorangeは既に作ってるし、あとはファイルを開き、書き込み用のファイルも開いて書き込むだけ。
なんで、nameを開く、そして書き込み用に、このケースだとberserk.htmlと言うファイルを作成して書き込みたい。そこで、拡張子部分の".txt"と".html"をreplaceメソッドで置き換えて書き込み用ファイルを作成する。

>>> "berserk.txt".replace(".txt", ".html")
'berserk.html'
>>>

そうすれば無事、berserk.htmlと言うファイルにgorangeが変換したberserk.txtの内容が書き込まれるわけ。


何も表示されてないけど(表示しろ、って命令してないから・笑)、無事、HTMLファイルは作成されています。



中身はこんなカンジ。



今回は改行とか特に指定しなかったんで、こんなカンジですが、berserk.htmlをダブルクリックすればブラウザでどんな風に表示されるか確認出来るでしょう。


ほい、これで完全に「出来上がり」です。pipみたいに端末上で司令するんで、如何にも本格的なプログラムでしょ?

今回の指南は次の二つが中心です。

  1. 関数はなるべく小さい「大した事ないな」と思える機能から順番に作っていく。
  2. リスト内包表記にビビらない。forwhileに頼らなくても80%はリスト内包表記で解決可能だ、と言う事を肝に命ぜよ。
特に2番。Pythonのリスト内包表記はぶっちゃけ、forwhileよりも速いです。
それは何故か、と言うとPythonの開発者達はリスト内包表記を最適化してます。何故にわざわざ苦労して最適化してるのか、と言うと、ユーザーにリスト内包表記をバンバン使って欲しい、って思ってるから。じゃないとわざわざ苦労して最適化しねぇよな。
そしてプログラミング言語は言語設計者の意図したように使うのが重要。従って、forwhileを使いそうな局面に来た時、リスト内包表記を代わりに使えないか、毎回自問した方が良い。じゃないと言語設計者の意図に反したプログラムを書くハメになって、それはある意味「間違ったプログラムを書いてる」と言う事になります。

6. 落ち穂拾い

あと、リストのインデックスが0から始まるのもFor使う時に混乱する(今更)。なんか理由あるんだろうけどな~・・

無いです(笑)。敢えて言うとC言語由来。
ちなみに、PascalとかBASICは配列は1から数えるんで、「0から数え始めるのが絶対」ってワケじゃない。そして恐ろしい事にVBAだと両者混在してるんでワケワカメ・・・・・・。
ただし、敢えて言うとリストのインデックスなんぞ忘れろ(笑)。
別の人も、リストのインデックス頼りでforのコードを書いてるんだけど、これって悪習なんだよなぁ。

種明かしすると。C言語バリバリやってきました、って人がPythonにやってきて、初心者用に教材書くと、「C言語流に」インデックス多用したコードをまずは教えたがるんだよ。これは間違いなんだ。だから通常はrange関数とか忘れてて構わない。むしろ使うな
forはC言語にもあるんで似てるように思うかもしれないけど、全然違う。これは基本的には、データ型の方に組み込まれてるイテレータ(反復子)を発動させる為のキーワードなのね。
Java知ってるかどうか知らないけど、どっちかっつーとPythonのforはC言語のforよりJavaの拡張forに近い。添字でデータ要素を指定するんじゃなくって、データ要素そのものを取り出すのがPython流のforの使い方としては正しいのです。
だから良くあるプログラミング初心者用の教材で、

for i in range(len(datalist)):
     print(datalist[i])

みたいなコード例紹介されてるけど、動くけどこういうコード例はC的には正しくてもPythonだと間違いです。
以下のように書くべき。

for i in datalist:
 print(i)

添字のメンド臭さから解放する為にPythonのforは設計されてるんで、それに従いましょう。直接データ要素を取り出して操作出来るのがPythonのforの強み
脱初心者を狙うのなら、for絡みでrange使いそうになったら立ち止まる事。「本当にそのやり方は必要か?」と。毎回自問するようにしましょう。

とまぁ、今回の「余計なお世話」はこんなトコかな?
最後にプログラム全体を載せておこう。

#!/usr/bin/env python3

import sys

table = {"head": "<!doctype html><html><head><title>{}</title></head><body>",
    "div" : "<div>{}</div>",
    "tail": "</body></html>",
    "a": "<span style=\"background-color:rgb(255, 147, 128);\">{}</span>",
    "e": "<span style=\"background-color:rgb(153, 194, 255);\">{}</span>",
    "i": "<span style=\"background-color:rgb(255, 255, 78);\">{}</span>",
    "o": "<span style=\"background-color:rgb(145, 242, 97);\">{}</span>",
    "u": "<span style=\"background-color:rgb(255, 128, 200);\">{}</span>"}

def rgb(s):
 try:
  return table[s.lower()].format(s)
 except KeyError:
  return s

def tail2rgb(s):
 baralist = s.split(" ")
 return " ".join(baralist[:-1]) + " " + "".join([rgb(i) for i in baralist[-1]])

def gorange(datalist):
 head = datalist[0].strip()
 tail = datalist[1:]
 return [table["head"].format(head)] + [table["div"].format(head)] +\
    [table["div"].format(j) for j in [tail2rgb(i.strip()) for i in tail]] +\
    [table["tail"]]

if __name__ == '__main__':
 name = sys.argv[1]
 with open(name, 'r') as f:
  with open(name.replace('txt', 'html'), 'w') as nf:
   nf.writelines(gorange(f.readlines()))
  • Xでシェアする
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

最近の「プログラミング」カテゴリーもっと見る

最近の記事
バックナンバー
人気記事