INITIALIZATION File URL: https://jonasbrosbucket.s3.us-east-2.amazonaws.com/3ce1ae59-87c4-4812-a805-d634c8bacf21/ThinkPython.pdf Namespace: 3ce1ae59-87c4-4812-a805-d634c8bacf21 Index Name: ki-dev-large ================================================== **Elapsed Time: 0.00 seconds** ================================================== FILE BYTES EXTRACTED FILE NAME: ThinkPython.pdf ================================================== **Elapsed Time: 0.97 seconds** ================================================== PDF EXTRACTION DONE ================================================== **Elapsed Time: 240.90 seconds** ================================================== INDEXING SUCCESS Content: [{'id': '31e79a33-4241-482c-ad0a-f943a1dd2d9f', 'page': 1, 'text': 'ThinkPython:コンピュータサイエンティストのように考えてみよう相川利樹(日本語訳)\x0c'}, {'id': '19ea9345-c8c5-4ea4-a6dd-367db76ad23c', 'page': 2, 'text': '2(原題)ThinkPython:HowtoThinkLikeaComputerScientist(著者)AllenB.DowneyCopyrightc(cid:2)2013相川利樹「ThinkPython:コンピュータサイエンティストのように考えてみよう」by相川利樹islicensedunderaCreativeCommons表示3.0非移植License\x0c'}, {'id': 'dc94baa8-aed7-4a9c-8944-56fad0bc046d', 'page': 3, 'text': '3目次はじめに11第1章プログラムが動くまで131.1プログラミング言語Python......................131.2プログラムとは何か..........................151.3デバッギングとは何か?........................161.3.1構文エラー...........................161.3.2実行時エラー..........................161.3.3意味的エラー..........................171.3.4実験科学的デバッギング....................171.4形式言語と自然言語..........................181.5初めてのプログラム..........................191.6デバッギング..............................201.7語句...................................211.8練習問題.................................23第2章変数、表式、文252.1値と型..................................252.2変数...................................262.3変数名と予約語.............................272.4演算子と被演算子............................282.5表式と文.................................292.6インタラクティブ・モードとスクリプト・モード..........292.7演算子の順位..............................312.8文字列処理...............................312.9コメント.................................322.10デバッギング..............................332.11語句...................................332.12練習問題.................................34\x0c'}, {'id': 'f1d5bc54-2c16-45ca-9aec-3594d95298db', 'page': 4, 'text': '4第3章関数373.1関数呼び出し..............................373.2型変換関数...............................373.3数学関数.................................383.4混合計算.................................393.5新規関数の追加.............................403.6関数定義とその利用法.........................413.7実行の流れ...............................423.8仮引数と実引数.............................433.9変数や仮引数はローカルである....................443.10スタック図...............................453.11結果を生む関数とボイド関数.....................473.12なぜ関数?...............................483.13from付きのインポート.........................483.14デバッギング..............................493.15語句...................................503.16練習問題.................................51第4章事例研究:インタフェース設計554.1カメの世界...............................554.2簡単な繰り返し.............................564.3練習問題.................................574.4カプセル化...............................584.5一般化..................................594.6インタフェース設計..........................604.7再因子分解...............................614.8開発計画.................................624.9ドキュメント文字列..........................634.10デバッギング..............................634.11語句...................................644.12練習問題.................................65第5章条件文と再帰675.1モジュラ演算子.............................675.2ブール代数表現.............................675.3論理演算子...............................685.4条件処理.................................69\x0c'}, {'id': '1a84b114-19cf-4677-9d02-6025099cd042', 'page': 5, 'text': '55.5二者選択処理..............................695.6条件文の連鎖..............................695.7入れ子の条件処理............................705.8再帰...................................715.9再帰関数のスタック図.........................725.10無制限な再帰..............................735.11キーボード入力.............................745.12デバッギング..............................755.13語句...................................765.14練習問題.................................77第6章結果を生む関数816.1戻り値..................................816.2段階的な改良法.............................826.3合成関数.................................846.4ブール代数関数.............................856.5再帰関数の拡張.............................866.6信用して跳び越える..........................886.7もう1つの例題.............................896.8型の検証.................................896.9デバッギング..............................916.10新しい語句...............................926.11練習問題.................................93第7章繰り返し処理977.1多重代入.................................977.2変数更新.................................987.3while文.................................987.4ブレイク.................................1007.5平方根..................................1017.6アルゴリズム..............................1037.7デバッギング..............................1037.8語句...................................1047.9練習問題.................................104\x0c'}, {'id': '3a04c9ab-d14c-4b2b-ae25-5485a54bfaab', 'page': 6, 'text': '6第8章文字列1078.1文字列は文字の配列..........................1078.2len....................................1088.3forループによる横断処理.......................1088.4文字列のスライス............................1108.5文字列は変更不可............................1118.6探索...................................1118.7ループ処理とカウンタ変数.......................1128.8文字列メソッド.............................1128.9in演算子................................1148.10文字列の比較..............................1158.11デバッギング..............................1158.12語句...................................1178.13練習問題.................................118第9章事例研究:単語あそび1219.1単語リストの読み込み.........................1219.2練習問題.................................1229.3探索...................................1239.4インデックス付きループ........................1259.5デバッギング..............................1269.6語句...................................1279.7練習問題.................................127第10章リスト13110.1リストは配列である..........................13110.2リストは変更可能............................13110.3リストの横断的処理..........................13310.4リストに対する演算..........................13410.5リストのスライス............................13410.6リストメソッド.............................13510.7写像・フィルタ・還元.........................13510.8要素の削除...............................13710.9リストと文字列.............................13810.10オブジェクトと値............................13910.11別名参照.................................14110.12リストを引数に使う..........................142\x0c'}, {'id': '5c046dcd-a9c4-450f-bca2-aa2affb21b61', 'page': 7, 'text': '710.13デバッギング..............................14310.14語句...................................14410.15練習問題.................................145第11章辞書14911.1カウンタの集合として辞書を使う...................15111.2ループ処理と辞書............................15311.3逆ルックアップ.............................15311.4辞書とリスト..............................15411.5メモ...................................15611.6大域変数.................................15811.7ロング整数...............................16011.8デバッギング..............................16011.9語句...................................16111.10練習問題.................................162第12章タプル16512.1タプルは変更不可............................16512.2タプルの代入..............................16612.3タプルを戻り値.............................16712.4可変長引数タプル............................16812.5リストとタプル.............................16912.6辞書とタプル..............................17012.7タプルの比較..............................17212.8配列の配列...............................17312.9デバッギング..............................17412.10語句...................................17512.11練習問題.................................176第13章事例研究:データ構造・選択17913.1単語頻度分布解析............................17913.2乱数...................................18013.3単語ヒストグラム............................18113.4頻度の高い単語.............................18213.5選択的な仮引数.............................18313.6辞書の差し引き.............................18413.7乱雑な単語選択.............................185\x0c'}, {'id': 'b08a9378-3f75-4022-a647-8d8e343a818d', 'page': 8, 'text': '813.8マルコフ解析..............................18613.9データ構造...............................18713.10デバッギング..............................18913.11語句...................................19013.12練習問題.................................191第14章ファイル19314.1永続性..................................19314.2読み込み・書き込み..........................19314.3記述演算子...............................19414.4ファイル名とパス............................19514.5例外捕捉.................................19714.6データベース..............................19814.7削ぎ落とし...............................19914.8パイプ..................................20014.9モジュールを書く............................20214.10デバッギング..............................20314.11語句...................................20414.12練習問題.................................205第15章クラスとオブジェクト20715.1ユーザ定義型..............................20715.2属性...................................20815.3長方形..................................21015.4戻り値としてのインスタンス.....................21115.5オブジェクトは変更可能........................21115.6コピー..................................21215.7デバッギング..............................21415.8語句...................................21515.9練習問題.................................215第16章クラスと関数21716.1時刻...................................21716.2純関数..................................21816.3修正関数.................................21916.4原型と開発計画.............................22016.5デバッギング..............................222\x0c'}, {'id': 'a59c5c23-6b1b-43ef-9df0-77813655ee7c', 'page': 9, 'text': '9\n16.6 語句. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 223\n16.7 練習問題. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 223\n第17 章クラスとメソッド\n225\n17.1 オブジェクト指向の特徴. . . . . . . . . . . . . . . . . . . . . . . . 225\n17.2 オブジェクトのprint . . . . . . . . . . . . . . . . . . . . . . . . . . 226\n17.3 別な例. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 228\n17.4 もっと複雑な例. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 228\n17.5 init メソッド. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 229\n17.6\nstr メソッド. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 230\n17.7 演算子の多重定義. . . . . . . . . . . . . . . . . . . . . . . . . . . . 230\n17.8 型別処理. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 231\n17.9 多態性. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 232\n17.10デバッギング. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 233\n17.11インタフェースと実装. . . . . . . . . . . . . . . . . . . . . . . . . 234\n17.12語句. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 235\n17.13練習問題. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 236\n第18 章継承\n239\n18.1 カードオブジェクト\n. . . . . . . . . . . . . . . . . . . . . . . . . . 239\n18.2 クラスの属性. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 240\n18.3 カードの比較. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 241\n18.4 積み札. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 243\n18.5 積み札のプリント. . . . . . . . . . . . . . . . . . . . . . . . . . . . 243\n18.6 追加・移送・シャ\nッフル・ソート\n. . . . . . . . . . . . . . . . . . . 244\n18.7 継承. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 245\n18.8 クラス図. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 247\n18.9 デバッギング. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 248\n18.10データカプセル化. . . . . . . . . . . . . . . . . . . . . . . . . . . . 249\n18.11語句. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 251\n18.12練習問題. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 251\n第19 章事例研究:Tkinter\n255\n19.1 GUI\n. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 255\n19.2 ボタンとコールバック. . . . . . . . . . . . . . . . . . . . . . . . . 256\n19.3 カンバス. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 257\n19.4 座標の配列\n. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 258\n'}, {'id': '4011f1d8-a3bf-4c80-bcde-d32aaf1e576a', 'page': 10, 'text': '1019.5さらなるウィジェット.........................25919.6パッキングウィジェット........................26019.7メニューとコーラブル.........................26319.8バインディング.............................26419.9デバッギング..............................26719.10語句...................................26819.11練習問題.................................269付録Aデバッギング273A.1構文エラー...............................273A.2実行時エラー..............................275A.3意味的エラー..............................279付録Bアルゴリズムの解析283B.1増加の次数...............................284B.2Pythonの基本操作の解析.......................286B.3探索アルゴリズムの解析........................289B.4ハッシュ表...............................289付録CLumpyついて295C.1状態図..................................296C.2スタック図...............................297C.3オブジェクト図.............................297C.4関数とクラスオブジェクト.......................300C.5クラス図.................................301付録D日本語の処理305D.1ユニコード文字列の生成........................305D.2エンコード方式の指定.........................306D.3ユニコード文字列のエンコード変換..................307D.4辞書やタプルで日本語.........................308D.5日本語を含むファイル.........................309訳者あとがき310\x0c'}, {'id': 'e52c29c0-1c40-428a-bb60-de9949312d6c', 'page': 11, 'text': '11はじめに本書の来歴1999年1月Javaを使ったプログラミング入門の講義の準備をしていた。これまで三回もこの講義を行っていたが、段々と嫌気が募ってきた。この講義の落第者の割合は高く、及第した学生の全体的な到達レベルもそんなに高くなかった。\u3000問題の一つは書籍だ。それらの書籍は余りにも大きく、必要以上にJavaの文法の細部にページを割き、如何にプログラムを組むかといった点に充分な組織的な配慮がなされてないものであった。それらの書籍の全てが「落とし穴」効果に犯されていた。つまり、最初は易しく、徐々に先に進むスタイルになっているが、第五章あたりから急に難しくなって床が抜けてしまう。学生たちは余りにも急に多くの新しいことにぶつかり、これ以降私は系統だった講義ができなくなり、切れ切れの講義をせざるをえないとい状況に陥る訳だった。\u3000講義開始の二週間前私は自分自身で本を書くことに決めた。目標は•でき得る限り短く。同じ内容ならば五十ページを割くより、十ページで済ます方が学生の負担は少ない。•語彙に注意する。呪文は最小限にし、語句は最初にキチンと定義して使う。•徐々に組み立てる。「落とし穴」を避けるため、難しいテーマはそれを細分化したステップで構成する。•プログラム言語ではなく、プログラミングに焦点を当てる。最小限に必要なものだけでJavaを構成する。本の題名は必要だった。そのとき心に浮かんだのが、「コンピュータサイエンティストのように考えてみよう」(“Howtothinklikeacomputerscientist”)である。\u3000私の第一版は荒削りなものであったが有効だった。学生たちはそれを読み、高度な話題や興味あるテーマにもクラスの時間を割けるほどの理解を示した。さらに、最も重要なことであるが、学生たちがそれらのテーマについての実習もできたことだ。私はこの本をGNUFreeDocumentationLicenseの下で出版した。このことはこの本のコピー、変更、さらに配布は利用者が自由にできることを意味していた。\x0c'}, {'id': 'e35b4d68-3989-40c3-8ba4-0fc7c480e008', 'page': 12, 'text': '12はじめに次ぎに起きたことは実にかっこいいことだった。バージニア州の高校教師、JeffElknerが私の本を引き継ぎ、それをPythonで書き直してくれた。そして、彼はその翻訳版を私に送ってくれた。私は私の書いた本を読んでPythonを学ぶという希有な経験をした。2001年にはGreenTeaPressからPython版の初版を出版した。2003年に私はOlinCollegeで教鞭を取り始め、そこで初めてPythonを教えることになった。やってみると、Javaのときと対照的に学生の躓きは少なく学習が進み、もっと興味有るテーマに取り組めるようになり、概して学生たちはより面白がった。九年間以上かけて私は間違いを修正、サンプルプログラムを推敲、そして練習問題に多くの材料を追加し、この本を充実したものにしよう努力してきた。その結果誕生したのがこの本である。少し控え目に書名はThinkPythonとした。主な変更点を以下列記する。•各章の終わりにデバッギングに関する節を設けた。これらの節はバグを避けること、バグの発見の仕方の技術を扱っている。•理解を助ける小テストからかなり本格的な課題までに渡り、多くの練習問題を追加した。そしてそれらの殆ど全てに解答を書いた。•一連の事例研究を追加した。それらは課題、その解法、議論からなる少し長めの例題である。そのいくつかは、私は自分のクラスのために書いた一連のPythonプログラム、Swampyを基本にしている。このSwampyはhttp://thinkpython.comから入手できる。•プログラム開発手法や基本的なデザインパターンの議論を拡張した。•デバッギング、アルゴリズムの解析、LumpyによるUML図についての付録を追加した。私はこの本を使うことで学習が楽しみになり、あなたの役に立ち少しでもコンピュータサイエンティストのように考えることができるようになることを期待する。AllenB.DowneyNeedham,MA\x0c'}, {'id': 'bfe88af5-9ee4-45e7-9660-4037d99fe582', 'page': 13, 'text': '13第1章プログラムが動くまでこの本の目標は、如何にしたらコンピュータサイエンティストのように考えることができるかをあなたに教えることである。そのような考え方は数学者、工学者、自然科学者のそれぞれの特徴を合わせ持っている。数学者のように、コンピュータサイエンティストは自分のアイデアをコンピュータ上で実限させるために形式言語を使う。工学者のように物ごとを設計し、部品を集めて一つのシステムを作り、さまざまの可能性の損得を評価する。また、自然科学者のように、複雑系の振る舞いを調べ、仮説を立て、予測を検証する。このコンピュータサイエンティストに求められる最も重要な能力は問題解決能力(problemsolving)である。問題解決能力は問題を定式化し、その解決について創造的に考え、その解決を明白にかつ正確に表現する能力のことである。徐々に明らかになるが、プログラミングを学習するプロセスはこの問題解決能力を耕す大変に貴重な機会である。この章を「プログラムが動くまで」とした理由もここにある。一面では、プログラムの学習それ自体は有意義な能力開発である。他面では、プログラミングは他の目的のための手段である。追々とその目的とは何かがはっきりしてくるはずだ。1.1プログラミング言語Pythonここで学ぶコンピュータ言語はPythonである。Pythonは高級言語(high-levelLanguage)の一つである。多分名前は聞いたことがあるだろうが、他の高級言語にはC、C++、PerlそしてJavaがある。機械語やアッセンブリ言語と呼ばれる低級言語(low-levelLanguage)もある。大まかに言って、低級言語で書いたプログラムだけがコンピュータで直接実行できる。従って、高級言語で書いたプログラムはそれを実行する前に加工プロセスが必要になる。この余分なプロセスは時間を食う、この点は高級言語の短所である。しかし、その長所は計り知れない。第一は、高級言語を使うプログラミングはずっと楽だ。高級言語を使って書いたプログラムは短い時間で書くことができ、短く読むことは楽であり、従って間違いが少ないことである。第二は、高級言語は移植性(portability)があることである。この意味は高級言語で書いたプログラ\x0c'}, {'id': '6e88184d-1f83-4b56-bb89-05ad5bc4b4c6', 'page': 14, 'text': '# 第1章 プログラムが動くまで\n\n## 図 1.1: インタプリタはプログラムを少し読み、実行する。これを支配に繰り返す。\n\n![インタプリタ図](path/to/image1)\n\n## 図 1.2: コンパイラはソースコードをハードウェアで実行可能なオブジェクトコードに翻訳する。\n\n![コンパイラ図](path/to/image2)\n\nプログラムは小さな変更では変異せず、異種のコンピュータで実行できることである。あるコンピュータのために低級言語で書いたプログラムは他のコンピュータのみで実行可能で、他の異種のコンピュータで実行するには書き換えなければならない。この研究のため、幼少でのプログラムは高級言語で書かれている。低級言語は特性したアプリケーションのためにのみ使用される。\n\n高級言語は実際にプログラムをコンパイラで実行する際に低級言語に訳す方法になる。つまり、インタプリタ(Interpreters)とコンパイラ(Compilers)である。インタプリタは高級言語で書いたプログラムを逐次実行することができる。そこではプログラムの一部を逐次実行することになる。図 1.1 はインタプリタの役目を示している。\n\nコンパイラはプログラムを翻訳し、プログラムが実行される前にそのプログラム全体を翻訳する。このような訳を持ち、高級言語で書かれたプログラムをソースコード(source code)と呼び、翻訳されたプログラムはオブジェクトコード(object code)または実行可能プログラム(executable)と呼ばれる。理解するためにはプログラムは再度の確認作業をしなければならない。\n\nPython で書かれたプログラムはインタプリタで実行されるので、Python はインタプリタ言語の一種と考えられる。そこで、インタラクティブインタプリタモード(interactive mode)とスクリプトモード(script mode)の2つのモードである。インタラクティブモードは、自分のモードを持ち、プログラム全体に対するインタプリタは対話形式で表示される。例えばこうだ:\n\n```plaintext\n>>> 1 + 1\n2\n```'}, {'id': '57181f49-3476-4947-aeb2-ba75ead5ee3a', 'page': 15, 'text': '1.2.プログラムとは何か15ここで山型の記号>>>はインタプリタがユーザに入力可能状態を示す入力請求記号(prompt)である。この例ではあなたがキーボードから1+1と入力しエンタキーを敲くとインタプリタは2と表示する。もう一つのモードでは、あなたはソースコードをファイルに保存し、インタプリタはこのファイルの中味、これをスクリプト(script)と呼ぶが、を実行する。習慣として、Pythonスクリプトは.pyで終わるファイルに書かれる。スクリプトを実行するためにはインタプリタにそのファイル名を知らせなければならない。あなたがスクリプトファイルdinsdale.pyを実行したいと思い、UNIXの端末画面で作業をしているのであれば、そこでPythondinsdale.pyと入力すればよい。その他の開発環境ではスクリプトの実行はこれと異なる。あなたのPythonの開発環境についてはhttp://Python.orgを参照しなさい。インタラクティブ・モードでの作業は直ぐに結果がでるのでソースコードの小さい部分の動作検証に向いている。しかし、数行に渡るようなものはスクリプトとしてファイルに書き出すべきである。こうすることでそのソースコードを修正することができ、いつでも実行できる。1.2プログラムとは何か一つのプログラム(program)とは如何に計算を進めるのかということを特定した一連の命令の集まりである。ここで計算といっているものは例えば方程式の解を求める、多項式の根を求めるなどの数学的なものかもしれないが、しかし、それは文書の中やプログラムの中(不思議に思えるが)のテキストを検索したり、置き換えをしたりすることでもある。異なったプログラム言語で細部は異なってみえるが、基本的な命令はどのような言語でも確認できる:入力:キーボード、ファイルそしてその他の様々な装置からデータを得る。出力:画面にデータを表示、ファイルやその他の装置にデータを書き出す。演算:加算や乗算のような基本的な数学的操作。条件実行:特定の条件を調べ、条件が満たされていると目的のコードを実行する。繰り返し:ある動作を繰り返し実行する。大抵の場合何かを少しずつ変えて実行する。信じられないことだが、これらはプログラムが持つべき機能の殆ど全てである。あなたが既に使ったどのプログラムも、見かけは複雑にみえても、ここで述べた基本的な機能によく似た命令で出来上がっているのだ。だからプログラミングとは全てを盛り込んだ大きなタスクをより小さい複数のタスクに分解し、上記の基本的な機能の一つとして実行できるほど細かな多数のタスクに分解することである。\x0c'}, {'id': '773663d3-af61-4266-a471-b2b21fdecce4', 'page': 16, 'text': '16第1章プログラムが動くまでここでの話は少し漠然としているかもしれない。アルゴリズム(algorithms)の話題のところで再度考察しよう。1.3デバッギングとは何か?\u3000プログラミングにはエラーは付き物だ。いたずら心から、プログラミングに起因するエラーはバグ(bugs)と呼ばれる。そしてこのバグ取りの過程をデバッギング(debugging)と呼ぶ。プログラムでは三種類のエラーが起こりうる。つまり、構文エラー、実行時エラー、意味的エラーである。これらの三種類のエラーを区別し、バグ取りが効果的に行えるようにするとよい。1.3.1構文エラー\u3000Pythonは構文は正しいときにのみプログラムを実行する。そうでないときにはインタプリタはエラーメッセージを吐き出す。構文(syntax)とはプログラムの構造に言及したもので、その構造が持つべきルールである。例えば、カッコは右左を揃えて使わなければならない、だから(1+2)は合法であるが、8)は構文エラー(syntaxerror)になる。\u3000\u3000英語では読者は大抵の構文エラーには寛容だ。だからE.E.Cummingsの詩はエラーメッセージを吐き出すことなく読める。Pythonは許さない。あなたのプログラムが構文エラーを一つでも含んでいると、Pythonはそのエラーに対してエラーメッセージを出し、そこでプログラムの実行を停止する。プログラミングの経験の浅い初めの数週間はこの種の構文エラーのバグ取りにかなりの時間を費やすだろう。しかし、経験を積むに従ってエラーをする度合いは減るし、エラーを容易く発見できるようになる。1.3.2実行時エラー第二のエラーはプログラムが実行されるまでは現れないので実行時エラーと呼ばれているものである。これらのエラーはプログラムを実行しているときに何か例外的(そして悪い)な状況が起きたときに出るエラーなので例外(exceptions)とも呼ばれている。\u3000簡単なプログラムでは実行時エラーは希であるので、それに遭遇するまでは暫くかかる。\x0c'}, {'id': '71aa4ae7-8dc8-4640-a074-0aafd1c48b70', 'page': 17, 'text': '1.3.デバッギングとは何か?171.3.3意味的エラー\u3000第三のエラーは意味的エラー(semanticerror)である。意味的エラーがあってもコンピュータはエラーメッセージを出さないでプログラムは成功裡に実行さたようにみえる。しかし、結果が間違っている。意図したことと別なことが実行された訳である。特に、間違っているにせよ、あなたがせよと命じたことが行われているからだ。問題は書き上げたプログラムがあなたが書こうとしたプログラムでないことである。プログラムの内容(従ってその意味)が間違っている訳である。この意味的なエラーを探し出すことはきわどい仕事である。なぜならば、コンピュータの出力結果をみてソースプログラムの間違いを探り当てるという後ろ向きの作業をしなければならないからだ。\u30001.3.4実験科学的デバッギングあなたが獲得しなければならない最も重要な能力の一つはデバッギングである。デバッギングはやる気を挫くものであるが、これはプログラミングの作業のなかで最も知的で、やりがいがあり、興味深い領域である。ある意味では、デバッギングは探偵の仕事に似ている。幾つかの手懸かりを頼りにその結果を引き起こした過程やイベントを特定しなければならない。また、デバッギングは実験科学に似ている。何が間違っているかについて一つの考えが浮かんだら、プログラムを修正し、実行してみる。この仮説が正しかったとすると、その変更の結果を予測てき、前に一歩進める。仮説が間違っていたとすると、再度仮説を立て直さなければならない。シャーロック・ホームズも指摘したように、「あなたが不可能を取り除いてしまったとき、どんなものが残ろうとも、それがありそうもないことであっても、残ったものが真実にちがいない」(コナンドイル「四つの署名」)。多くの人たちにとっては、プログラミングとデバッギングは同義語だ。つまり、プログラミングとは最終的に欲しい結果を生み出すプログラムを徐々に完成させるデバッギングの過程である。この考え方は、最初はほんの簡単なことができるプログラムを作成し、進捗するにつれて、何時もその限りでは動くプログラムを心がけつつ、これを徐々に変更、デバッグするということだ。例えば、Linuxはオペレーティング・システムの一つであり、ソースコードは数千行もあるプログラムであるが、LinusTorvaldsが嘗てIntel80386チップの動きを調べるために作成した一つの簡単なプログラムであった。LarryGreenfieldによれば、「Linuxの初期のプロジェクトの一つはprintAAAAと\u3000printBBBBの切り替えを行うプログラムであった。後にこれがLinuxへと進化した。」(TheLinux\x0c'}, {'id': '96eff329-e441-4fd7-bd85-3f90e50abadf', 'page': 18, 'text': '18第1章プログラムが動くまでUsers’GuideBetaVersion1)後の章でデバッギングやプログラミング作法についてもっと多くの示唆をすることにする。1.4形式言語と自然言語自然言語(naturallanguage)は英語、スペイン語、フランス語のように人々が話している言語である。それらは(人々はこれらに対してある規範を課そうとするが)人工的に設計されたものではない。これらは自然と進化したのだ。形式言語(formallanguage)はある特別の用途のために人工的に設計された言語である。例えば、数学で用いる記号は数や記号の間の関係を記述する上で適切な形式言語である。化学者は分子の化学構造を示すために形式言語を用いる。そして、さらに重要なことであるが、プログラム言語は計算を表現するために設計されてきた形式言語である。形式言語はその構文が厳格になる傾向がある。例えば、3+3=6は数学的には正しい構文で作られた文である。しかし、3+=3$6はそうでない。H2Oは化学的には正しい構文で作られた文である。2Zzはそうでない。構文のルールは二つ特徴物からなる、つまり字句(tokens)と文法(sturuc-ture)である。字句は単語、数、化学元素のような言語の基本要素である。3+=3$6が不正である一つの理由は$が数学的に正しい字句でないことである。また、2Zzでは省略形で表現される元素がないことである。構文エラーの二つ目は文の文法に関連するものだ。つまり、字句の並べ方のよるものだ。3+=3という文は+や=は正しい字句であるが、隣接して使うことは不正である。同じように、化学式では元素記号の後に添え字が付き前ではない。練習問題1.1\u3000不正な字句を含む文法的に正しい英文を書きなさい。また、もう一つの例として、有効な字句からなる文法的に不正な英文を書きなさい。あなたが英文や形式言語の文を読んでいるとき、あなたはその文の構文がどうなっているか確認しなければならない(自然言語のばあいはこれを無意識に行っている)。このような過程を構文解析(parsing)と言う。例えば、あなたは“Thepennydropped”という文を聞いたとしよう。あなたは“The“apenney”が主語で、“dropped”は述語であることを理解する。構文解析が終わると、この文が意味していることの理解に向かう。“penny”の意味、“drop”の意味が分かっていると、この文が言おうしたことが理解できる訳である。形式言語と自然言語は多くの共通点、字句、文法、構文、意味があるが、いくつかの違いがある:\x0c'}, {'id': 'a4935a57-3665-4fb4-b64e-d3acf9245dfe', 'page': 19, 'text': '1.5.初めてのプログラム19曖昧さ(ambiguity):自然言語は曖昧さに満ちあふれている。人々は文の中味に手懸かりを求め、他の情報を使ってこの曖昧さに対処している。形式言語は、文の中味に拘わらず、ほぼまたは完全に曖昧さを排除するように設計されている。冗長性(redundancy):自然言語では曖昧さがあることや誤解を避けるために、冗長性を高めている。その結果、自然言語はだらだらしたものになる。形式言語は冗長性を減らしその結果簡明なものになっている。逐語性(literalness):自然言語は慣用句や比喩に満ちあふれている。わたしは“Thepennydropped”と言ったとしても、もしかするとそこには“penny”も“drop-ping”もないかもしれない(この慣用句的な表現は“目から鱗が落ちる”という意味だ)。一方、形式言語ではそこで言われたことがそのままの意味になる。自然言語を喋って育った人々は形式言語に慣れることに多々苦労する。ある意味では、この相違は詩と散文の違いに似ているが、それ以上だ:詩(poetry):単語はその意味と同時に音韻のためにも使われる。詩全体によって感情の起伏を作り出す。曖昧さの高さは一般的ばかりでなく、意図的だ。散文(prose):単語の逐語的な意味はもっと重要になり、文法はより意味を持つようになる。散文はより素直に解析できるが、多くのばあい曖昧さは多い。プログラム(program):一つのコンピュータプログラムが持っている意味は唯一で曖昧さがなく、字句と文法の解析で完全に理解できる。ここでプログラム(その他の形式言語でも)を読む上で留意すべき点を列記する。第一に形式言語は自然言語に比較して稠密なことだ。そのためそれを読むためには時間を要する。また、文法は極めて重要だ。従って、上から下、右から下へと読み下すことは薦められない。そのかわり、字句を特定し、文法的な解釈を頭でしながら、構文解析をすべきだ。最後に、細かなことであるが、自然言語では見過ごしてもよいような綴りや句読点の僅かな間違いが形式言語では重大な相異を生み出す。1.5初めてのプログラム伝統的に新しい言語であなたが書く最初のプログラムは“HelloWorld!”と呼ばれるものである。そのプログラムがすることの全ては画面に“HelloWorld”と表示することだからである。Pythonでは以下のようにある。print’HelloWorld!’これが紙の上に何も印刷しないけれど、print文(printstatement)の一つの例である。print文は一つの値を画面に表示する。いまの場合それは単語で、HelloWorld!\x0c'}, {'id': 'a70595f7-84d0-4260-b775-b34b6507191d', 'page': 20, 'text': '20第1章プログラムが動くまでである。プログラムでテキストの最初と最後を示す引用符号は結果には表示されない。Python3では構文が少し異なりprint(’HelloWorld!’)括弧はこのprintが関数であることを示す。第三章で関数は詳しく触れる。この本ではわたしはこのprint文を使う。もしあなたはPython3を使っているのであれば、読み替えをしてほしい。大したことではないので気にする必要はない。1.6デバッギング\u3000この本を読むときには、あなた自身で例題がどう動くか確認しつつ進めるようコンピュータを前にして読んでほしい。例題の多くはインタラクティブ・モードで実行できるが、いろんな試みをしてみるためにはスクリプト・モードの方が便利だ。\u3000新しいテーマに出会うたびに、間違いをすべきである。例えば、“HelloWorld!”プログラムである。print文で使っている引用符(’)の一つがないと何が起きるか?二つないと何が起きるか?printの綴りを間違えると何がおきるか?\u3000このような間違いを犯した経験は、このような間違いをするとそのとき表示されるエラーメッセージの意味を確認できるので、この本で読んだことが何であったかの記憶を助けてくれ、デバッギングの助けにもなる。早い内に、それも意図的に間違いをしてみる方は後に偶然にそれをするより賢明だ。\u3000プログラミングや特にデバッギングはときとして心理的な葛藤を呼び起こすことがある。もしあなたが困難なバグに格闘しているとすると、怒り、失望、恥ずかしさの感情を持つかもしれない。\u3000人々がこのような状況におかれるとコンピュータがあたかもヒトであるように対応することはよく知られていることである。作業が上手くいっていると我々はコンピュータを僚友と感ずる、そして、コンピュータが意地悪で、言うことを聞いてくれないと、我々はそのようにヒトに対して振る舞うように、コンピュータに対しても振る舞う(ReevesandNass,“TheMediaEquation:HowPeopleTreatComputers,Television,andNewMediaLikeRealPeopleandPlays”)。このようなヒトの反応に対して心構えをしておくことは賢明だ。一つの接近法はコンピュータを例えば、速度や精度に対して特殊な能力を持っているが、物事を斟酌し、大枠を捉えることに極端に弱点のある従業員と考えることである。あなたの仕事は優秀なマネージャであることである。つまり、その長所を活かし、弱点を補強する方法を探すことだ。そして、コンピュータに対するあなたの\x0c'}, {'id': 'b11879b9-32dc-4550-a0fb-65da258712ce', 'page': 21, 'text': '1.7.語句21感情が仕事をする上で邪魔にならないよう、あなたの感情を問題解決することに集中する方法を探すとよい。デバッグの方法を学ぶことは期待したものではないかもしれない。しかし、これはプログラミングという枠を超えて多くに仕事にも役にたつものである。各章の終わりにここで述べたようなデバッグの節を設けた。あなたの助けになればなによりだ。1.7語句問題解決能力(problemsolving):問題を定式化し、解を見つけそしてそれを表現する過程。高級言語(high-levelLanguage):Pythonのような、人にとって読み書きが容易になるように設計されたプログラム言語。低級言語(low-levelLanguage):コンピュータにとって実行が容易であるよに設計されたプログラム言語。「機械語」とか「アセンブリ言語」と呼ばれる。移植性(portability):一種以上のコンピュータ上で実行可能な性質。インタプリタ(Interpreters):高級言語のプログラムを一回に一行ずつ解釈し、実行すること。コンパイラ(Compilers):高級言語で書かれたプログラムを低級言語に一時に翻訳すること。翻訳された低級言語のプログラムは後に実行される。.ソースコード(sourcecode)::コンパイルされる前の高級言語で書かれたプログラム。オブジェクトコード(objectcode):ソースコードをコンパイルした結果生成されるプログラム。実行プログラム(executable):実行可能なかたちになったオブジェクトコードの別名。入力請求記号(prompt):インタプリタでユーザからの入力を受けることが可能になっていることを示すための文字列。スクリプト(script):一つのファイルとして保存されたプログラム(通常はインタプリタで実行される)。\x0c'}, {'id': 'f98e3e70-ee13-4866-9321-6b089ffbb8f5', 'page': 22, 'text': '22第1章プログラムが動くまでインタラクティブ・モード(interactivemode):入力請求時にコマンドや表式を入力することによってPythonインタプリタを使う方式。スクリプト・モード(scriptmode):一つのスクリプトを読み込みそれを一気に実行するようなPythonインタプリタの使い方。プログラム(program):一連の計算を実行するための一連の命令の集まり。.アルゴリズム(algorithms):典型的な諸問題を解く一般的な手続き。バグ(bugs):プログラムに潜んでいるエラー。デバッギング(debugging):プログラムに潜む三種類のエラーを見つけ取り除く作業。構文(syntax):プログラムの構造。構文エラー(syntaxerror):構文解析が不可能になるようなプログラム上のエラー(そしてそれ故インタプリタもエラーを出す)。例外(exceptions):プログラムの実行時に発生するエラー。意味論(semantics):プログラムの意味。意味的エラー(semanticerror):プログラムが意図した以外の別な内容を実行してしまうこと。自然言語(naturallanguage):人類の進化と共に進化したヒトが話す言語。形式言語(formallanguage):数学的な考えやコンピュータのためのプログラムのように特別な目的のために設計された言語。そして全てのプログラミング言語は形式言語である。字句(tokens):自然言語の単語に類似したプログラムの意味的構造上の基本要素の一つ。文法(sturucture):構文解析(parsing):形式文法に従ってプログラムを解析すること。print文(printstatement):Pythonインタプリタが画面上に値を出力することを意図した命令。\x0c'}, {'id': 'afc74977-ad34-4d38-af7c-6138c69012eb', 'page': 23, 'text': '1.8.練習問題231.8練習問題練習問題1.2Pythonのウエッブサイトhttp://python.orgを閲覧してみなさい。このページにはPythonの情報やPythonに関連する情報のリンクがある。そして、Pythonの関連する文献を検索することができる。たとえば、検索窓でprintと入力すると、その文献の中のprint文に関するリンクが表示されるはずである。そこの書かれている全ては理解できなくとも、どこを探せばよいかわかったことはよいことである。練習問題1.3Pythonインタプリタを起動してhelp()と入力してヘルプ機能を起動させてみなさい。また、help(’print’)と入力してprint文についての情報を得てみよう。上手く動作しなにならば、追加の文献が必要かもしれない。また環境変数の設定に問題があるのかもしれない。練習問題1.4Pythonインタプリタを起動し、計算をしてみよう。Pythonの算術記号に対する構文はほぼ数学的な記号と同じである。例えば、+,-,/の記号は加算、減算、除算の記号である。乗算記号は*である。あなたが10kmレースを43分30秒で走ったとしよう。1マイルあたり掛かった平均時間はいくらか?また、マイル毎時で表現した速度はいくらか?(ヒント:1マイルは1.61kmである)。\x0c'}, {'id': 'fc3ed911-4a8c-45ba-9622-094e39953631', 'page': 1, 'text': ''}, {'id': 'f7a4906f-3139-4ce4-a3b5-dcc586772b59', 'page': 25, 'text': '25第2章変数、表式、文2.1値と型値(value)は文字や数値のようなプログラムに関わる基本的なものである。これまでに出会った値は1、2それに’HelloWorld!’である。これらの値は異なった型(types)に属している。つまり2は整数(integer)で、’Helloworld!’は文字の列を含んでいるので、文字列(string)である。引用符で括られているのであなたも(インタプリタも)これが文字列と同定できる。ある値がどの型であるか不明なときはインタプリタが教えてくれる:>>>type(’HelloWorld!’)>>>type(17)当然だが、文字列はstr型に属し、整数はint型に属する。少し複雑になるが、小数点を含む数値は浮動小数点数(floatingpoint)という形式で表現されるので、float型に属する:>>>type(3.2)ところで、’17’や’3.2’は何型かな?\u3000数値のようにみえるが、文字列のように引用符で括られている。>>>type(’17’)>>>type(’3.2’)これらはstr型に属する、つまり、文字列である。大きな数を表示するとき、1,000,000の様に三つの数字毎にカンマを入れることがある。これはPythonの世界では不正な整数であるが、合法的だ。\x0c'}, {'id': '6d0082a8-a175-4980-861c-1f2d19252bf3', 'page': 26, 'text': '26第2章変数、表式、文>>>1,000,000(1,0,0)まあ、期待したものではないが。Pythonインタプリタはカンマで区切られた三つの整数だと解釈する。これは、我々は最初に経験した意味的エラーである。コードはエラーメッセージ無しで実行できた。しかし、結果は期待したものなかったわけだ。2.2変数プログラム言語の強力な機能の一つは変数(variable)が扱えることだ。変数は値を参照するための付けられた名前である。代入文(assignmentstatement)によって変数は生成され、それに値が与えられる:>>>message=’Andnowforsomethingcompletelydifferent’>>>a=17>>>pi=3.141592653597932これらの例では三つの代入文を生成した。最初の例では文字列の代入を新しい変数messageに、二番目では整数17の代入を変数aに、最後の例では、円周率πの近似値を変数piに代入した。messagenpi17’And now for something completely different’3.1415926535897932図2.1:状態図\u3000紙の上で変数を表現するために良く用いられる方法は新しい名前を書きその変数が持つ値に向けて矢印を書くというものだ。この種の図は各変数の現在の状態(心の変動状態のように考えて)状態図(statediagram)と呼ぶ。図2.1は前の例の状態図である。変数の型はそれを参照する値の型である:>>>type(message)>>>type(a)\x0c'}, {'id': '168aecd9-b71b-49a3-bbff-ea2d1c2e18a0', 'page': 27, 'text': '2.3.\n変数名と予約語\n27\n\n>>> type(pi)\n\n練習問題2.1 \u3000整数を先頭の0 を付けて代入すると、あなたは少しまごつくエラー\nを得るはずだ:\n>>> zipcode=02492\nSyntaxError: invalid token\n他の例を試すとエラーはないが、結果がおかしい:\n>>> zipcode=02132\n>>> zipcode\n1114\n何が起きているか分かるかな?ヒント:01、010、0100、01000 等の値で試してみ\nよう。\n2.3\n変数名と予約語\nプログラマーは一般的にそれが何のために用いられているかが分かるように意\n味がある変数名を選ぶ。\n変数名はいくらでも長くできる。それらは文字と数字を含んでいても構わない\nが、先頭は文字でなければならない。大文字も合法的だが、先頭文字は小文字に\nするのが賢明だ(理由は後で分かる)\n。\nアンダースコア文字、’ ’ も名前に付けてよい。これは例えば、\nmy name やairspeed of unladen swallow のように、単語を結びつけて使うとき\nに頻繁に使う。\nあなたが不正な名前を使うと構文エラーになる:\n>>> 76trombones = ’big parade’\nSyntaxError: invalid syntax\n>>> more@ = 1000000\nSyntaxError: invalid syntax\n>>> class = ’Advanced Theoretical Zymurgy’\nSyntaxError: invalid syntax\n'}, {'id': '90f1bfdc-9a88-454d-9295-33dfbf6615dc', 'page': 28, 'text': '28第2章変数、表式、文76trombonesは先頭が文字で始まらないので不正な名前である。more@は不正な文字を含むので不正である。では、classは?実は、classはPythonの予約語(keywords)である。インタプリタはプログラムの構造を認識するためにこれらの予約語を使い、変数名として使うことができない。Python2は31個の予約語がある:anddelfromnotwhileaselifglobalorwithassertelseifpassyieldbreakexceptimportprintclassexecinraisecontinuefinallyisreturndefforlambdatryPython3ではexecは予約語ではなくなって、nonlocalが予約語になった。あなたはこのリストを手元の置きたいと思うかもしれない。インタプリタは変数名に対して苦情を言っているのに理由が不明なときはこのリストをみてほしい。2.4演算子と被演算子演算子(operators)は加算、乗算、階乗算のような計算を表現する特別な記号である。演算子が作用する値は被演算子(operands)と呼ばれる。演算子+、-、*、/、**は以下の例のように加算、減算、乗算、除算そしてべき乗算を実行する:20*32hour-1hour*60+minuteminute/605**2(5+9)*(15-7)他の言語では、記号^がべき乗算に用いられるが、PythonではXORというビット単位の演算子として用いられる。この本ではこのテーマには触れないが、興味ある読者はhttp://wiki.python.org/moin/BitwiseOperatorsを眺めてほしい。Python2では除算演算子はあなたが期待した通りにならないかもしれない。例えば>>>minute=59>>>minute/600\x0c'}, {'id': 'fc10d1e5-a0dd-40e2-a342-29a33c016d5c', 'page': 29, 'text': '2.5.表式と文29変数minuteの値59、従って伝統的な算術59dividedby60の結果は0.98333であり、0ではない。この食い違いはPythonでは打ち切り除算(floordivision)が行われるからだ。両方の被演算子が整数であると結果の整数になる。打ち切り除算は小数点以下を打ちきる。だから結果はzeroである。Pyhton3ではこの除算の結果は浮動点小数点である。新しい演算子//を使うと打ち切り除算となる。被演算子のどちらかが浮動点小数点数であるとPythonは浮動点小数点の計算をし、結果は浮動点小数点となる:>>>minute/60.00.983333333333333282.5表式と文一つの表式(expression)は値、変数、演算子の組み合わさったものである。一つの値のみも表式であり、変数一つもそうである。従って以下の表式は合法的である(変数xには予めある値が代入されたとしてだが):17xx=17一つの文(statement)はPythonインタプリタが実行できるソースコードの単位である。これまで我々はprint文と代入文の二つ文に出会った。技術的には、表式も文ではあるが別ものと考える方が簡単だ。重要な違いは、表式は値を持つが文は値を持たないことだ。2.6インタラクティブ・モードとスクリプト・モードインタプリタ言語で作業をする一つの大きな利点はソースコードをスクリプトとしてまとめて書く前に、そのコードを少しずつテストできることだ。しかし、インタラクティブ・モードとスクリプト・モードの間には若干の相異があり、混乱を引き起こすかもしれない。例えば、以下はPythonを電卓のように使う例である:>>>miles=26.2>>>miles*1.6142.182\x0c'}, {'id': '7f4b0797-5746-4167-b293-a85321c9e825', 'page': 30, 'text': '30第2章変数、表式、文一行目は変数milesへの代入文であり、結果はなにも表示されない。二行目は表式で、インタプリタはこの表式を評価し、その結果を表示する。これでマラソン競技は約42kmの距離を走ることが分かる。しかし、この二行をそっくりスクリプトとして書き、実行しても何も表示されない。スクリプト・モードでは表式それ自体には表示機能がない。スクリプト・モードでも表式は評価されるが、表示は別だということである。スクリプトでは以下のように書く:miles=26.2printmiles*1.61最初はこのことで混乱するかもしれない。スクリプトは通常複数の文を含む。複数の文があるときは、結果はそれらの文が一度に一つずつ実行される。スクリプトの例を示す:print1x=2printxこれを実行した結果の表示は12となる。代入文には表示するものがない。練習問題2.2\u3000以下のような表現をPythonインタプリタで入力し、何が表示されるか調べよ。5x=5x+1さて、同じ表現をスクリプトとして書き、そのスクリプトを実行してみよ。何が今回は表示されるか調べよ。スクリプトを修正して、各表式をprint文に直し実行してみよう。\x0c'}, {'id': '33764be9-6a79-4093-a489-3878ef5a042f', 'page': 31, 'text': '2.7.演算子の順位312.7演算子の順位表式に一つ以上の演算子が含まれていると、その表式の評価は優先順位(ruleofprecedence)による。数学的な演算子については、Pythonは数学的慣用に従う。PEMDASという頭字語として覚えるとよい。•括弧は最も高い優先度を持つ、そしてこれによってある表式の評価をあなたが必要とする順位で処理させることができる。括弧で括られた表式が最初に評価されるので2*(3-1)は4であり、(1+1)**(5-2)は8である。また、あなたは括弧を表現の見やすさのために用いることもできる。(minute*100)/60では括弧はなくても結果は同じになる。•階乗算は次ぎに高い順位を持つ演算子である。だから、2**1+1は3であり、4ではない。また、3*1**3は3であり、27ではない。•乗算、除算は加算、減算(この二つも同じ優先度である)より優先度が高く、同じ優先度を持つ。だから、2*3-1は5であり、4ではない。同じく、6+4/2は8であり、5ではない。•同じ優先度を持つ演算子は左から右へ(例外は階乗算)と評価される。だから、表式degree/2*piは除算が最初で、その結果にpiがかけられる。2πで割ることを意図したのであれば、括弧を使うか、degree/2/piとする。わたしは他の演算子についての優先順位を憶えることにあまり熱心でない。表式がそれらしくみえないときは、括弧を使い明確にすることにしている。2.8文字列処理一般に算術的な演算をそれが見かけの上で数のようであっても文字列に施すことはできない。従って、以下のような演算は不正である:’2’-’1’’eggs’/’easy’’third’*’acharm演算子+は文字列に作用できるが、あなたが期待したものではないかもしれない。これは文字列の連結(concatenation)、つまり、文字列と文字列の端と端を結合することである。例を挙げるとfirst=’throat’second=’warbler’printfirst+second\x0c'}, {'id': 'd92202bf-02da-4dde-97cd-934ce4f8f771', 'page': 32, 'text': '32第2章変数、表式、文表示されるものはthroatwarblerである。演算子*も文字列に使え、文字列の繰り返しを行う。例として、’Spam’*3はSpamSpamSpamの表示を得る。被演算子の一つが文字列ならば、他は整数というかたち使う。これらの演算子+、*の使用法は算術演算子として持つ機能を考えれば納得がゆくものであろう。4*3が4+4+4と同値であるように、’Spam’*3は’Spam’+’Spam’+’Spam’であろうからである。一方、この文字列の連結処理や繰り返し処理と整数の加算、乗算とは異なるところがある。文字の連結ではできないが整数の加算ではできる性質の一つを思い浮かべることができるか?2.9コメントプログラムが大きくなり、より複雑になるとプログラムを読むことが難しくなる。形式言語は稠密なので、コードの一部分を眺めてそこで行われていることを理解することが多々困難なことがある。そこでプログラムの中に自然言語でそこで行われていることについての注釈を追加することは賢明である。これらはコメント(comments)と言われ、記号#で始まる:#computethepercentageofthehourthatareelapsedpercentage=(minute*100)/60この例ではコメントは一行に書いた。行の終わりに置くこともできる:percentage=(minute*100)/60#percentageofanhour記号#から行の終わりまでがコメントで、プログラムには何の影響も持たない。コメントはそのコードの部分に自明でない特徴がある場合に有効である。コードを直接眺めることでそこで何が行われているかが分かる方が説明を追加するより好ましいことには論を待たない。以下のコメントは冗長で無意味である:v=5#assign5tov以下のコメントはコードのみでは得られない情報を含むので有用だ:v=5#velocityinmeters/second.変数の付け方が上手いとコメントの量を少なくできるが、長い名前は表式を複雑にさせ読み辛いものになるので、損得を天秤かける必要がある。\x0c'}, {'id': 'fc3afcc4-a0ab-47a4-8d0f-b2fd63ddc96b', 'page': 33, 'text': '2.10.デバッギング332.10デバッギングここまでで出会いそうな構文エラーは不正な変数名、classやyield(これらは予約語)またはodd~jobやUS$(不正な文字を含む)についてだろう。\u3000また空白を含む文字を使うと、Pythonは二つの被演算子が演算子なしで用いられていると解釈する。>>>badnameSyntaxError:invalidsyntax構文エラーに関して、このエラーメッセージはあまり役に立たない。頻繁に出会うエラーメッセージはSyntaxError:invalidsyntaxやSyntaxError:invalidtokenで、これらは情報不足である。実行時エラーでよく遭遇するのは、「定義前の使用」だ。つまり、値を代入する前に変数を使うことによるエラーだ。よくやるのは変数名の綴りミスによるものだ。>>>principal=327.68>>>interest=principle*rateNameError:name’principle’isnotdefinedまた、変数名は大文字小文字で異なる。だから、LaTeXとlatexは異なる。これまでの時点で遭遇しそうな意味的エラーは演算子の順序に関連するものだろう。例えば、1/2πを評価しようとして以下のように書いたとしよう:>>>1.0/2.0*pi最初に除算がなされるので、π/2が得られる。これは意図したものと異なる。Pythonはあなたが何を意図してこれを書いたか知る手段がないので、勿論エラーメッセージは出ない。間違った結果を出すだけだ。2.11語句値(value):プログラムが操作する数値や文字列のような基本的データの実体。型(types):値が持つ属性。これまで出会ったのは整数(int型)、浮動小数点数(float型)そして文字列(str型)である。整数(integer):全ての数を表現する型。浮動小数点数(floatingpoint):小数点以下を持つ数値を表現する型。\x0c'}, {'id': 'd664ab76-a832-4dcf-b57a-b9430f6299a2', 'page': 34, 'text': '34第2章変数、表式、文文字列(string):文字の列を表現する型。変数(variable):一つの値を参照するための名前。文(statement):一つの命令や動作を表現するコードの一段落。これまでに出会ったのは代入文そしてprint文である。代入文(assignmentstatement):一つの値を一つの変数に割り当てる文。状態図(statediagram):値の一組とそれらを参照する変数の一組の関係を示すグラフィックな表現。.予約語(keywords):コンパイラがプログラムを構文解析するために予め使うことを予定してある単語。あなたのプログラムではif、defそしてwhileのような単語を変数名として使うことができない。.演算子(operators):加法、乗法そして文字列の連結などの単純な計算を表現する特な記号。被演算子(operands):演算子が作用する値。打ち切り除算(floordivision):二つの数の除算をし小数点以下を切り捨てる操作。表式(expression):変数、演算子そして値を組み合わせて一つの計算を表現した式。結果は一つの値になる。.優先順位(ruleofprecedence):複数の演算子とを含む表式の評価(結果の値を出す)する際に適用される演算の順序に関する規則。連結(concatenation):二つの被演算子を端と端で繋ぐこと。コメント(comments):他のプログラマー(またはそのソースコードを読む)人たちのためにプログラムに書き込む情報。プログラムの実行には何の効果も無い。2.12練習問題練習問題2.3\u3000以下の代入文は実行済みと仮定しなさい:\x0c'}, {'id': '443c58d9-44cf-42f1-b8ce-9dd9cfe89cb1', 'page': 35, 'text': '2.12.\n練習問題\n35\nwidth = 17\nheight = 12.0\ndelimiter = ’ ’\n以下のような表式の値を評価しなさい、そして得られる値の型も示せ。\n1. width/2\n2. width/2.0\n3. height/3\n4. 1 + 2 * 5\n5. delimiter * 5\nPython インタプリタを使ってあなたの答えをチェ\nックしなさい。\n練習問題2.4 \u3000Python インタプリタを電卓として使い以下の計算をしない。\n1. 半径r の球の体積は4\n3πr3、半径5 の球の体積はいくつか?\n(ヒント:392.7\nは間違い)\n2. ある本の定価は24.95 ドルだが、本屋は40%の割引をしている。送料は最初\nの一冊では3 ドルで、二冊目以降は75 セントである。60 冊買うとして総額\nはいくらになるか?\n3. わたしは6:52am に家を出て、ゆっくりした歩行(8 分15 秒毎マイル)で1\nマイル歩き、普通の歩行(7 分12 秒毎マイル) で3 マイル歩き、再びゆっく\nりした歩行で1 マイル歩いて帰宅したとすると何時に朝食ができるか?\n'}, {'id': '0c2af71e-50cc-402a-9d47-526353aa7cf5', 'page': 1, 'text': ''}, {'id': '6128597b-e98f-4ad0-a769-cb0eef646446', 'page': 37, 'text': '37第3章関数3.1関数呼び出しプログラミングの範疇で言うと関数(function)とはある計算を実行する文の名前付きの一括りである。関数を定義するということは、名前を付けることと、一括りの文を定義することである。こうすると後にこの関数を名前で「呼び出す」ことができる。既にわれわれはこのような関数呼び出し(functioncall)の一例をみてきた。>>>type(32)この関数の名前はtypeである。括弧の中の表式は関数の引数(argument)と言われるものである。この関数は結果としてこの引数の型を表示する。一般の関数は引数を「受け取り」、結果を「返す」。この結果のことを戻り値(returnvalue)と言う。3.2型変換関数Pythonは値の型を他の型に変換する組み込み関数群を提供している。int関数は任意の値を受け取ることができ、可能なときはその型を整数に変換する。さもないと、苦情を吐き出す:>>>int(’32’)32>>>int(’Hello’)ValueError:invalidliteralforint()withbase10:’Hello’また、int関数は浮動点小数も整数に変換するが、それは四捨五入ではなく小数点以下切り捨てになる。\x0c'}, {'id': 'afe574c2-6740-41a6-96c0-fd20ae342e49', 'page': 38, 'text': '38第3章関数>>>int(3.99999)3>>>int(-2.3)-2float関数は整数や文字列を浮動小数点数に変換する:>>>float(32)32.0>>>float(’3.14159’)3.1415899999999999最後の関数であるstr関数は引数を文字列に変換する:>>>str(32)’32’>>>str(3.14159)’3.14159’3.3数学関数Pythonは馴染み深い数学関数を一つのモジュール(module)として提供している。モジュールとは関連する関数類を一纏めしたファイルである。モジュールは使う前にそれをインポートしなければならない:>>>importmathこの文でmathと言う名前のモジュール・オブジェクト(moduleobject)が生成される。モジュール・オブジェクトをprint文で印字とすると、このモジュールの情報が得られる:>>>printmathこのモジュール・オブジェクトはこのモジュールで定義された関数や変数を沢山に含んでいる。それらの関数の一つにアクセスするには、モジュール名と関数名をドット(終止符として知られている)で区切って特定しなければならない。この記法はドット表記(dotnotation)と言う。\x0c'}, {'id': '61e7d653-0ab4-49d5-b08c-b83820082978', 'page': 39, 'text': '3.4.\n混合計算\n39\n>>> ratio = signal_power / noise_power\n>>> decibels = 10 * math.log10(ratio)\n>>> radians = 0.7\n>>> height = math.sin(radians)\n最初の例では関数log10 と信号・雑音比をデシベルで計算するために使った(勿\n論、signal power、noise power は定義済みとしている)\n。数学モジュールは同様\nにlog 関数、つまり底をe とする対数も提供している)\n。\n二番目の例ではラジアンのsin である。変数の名前がヒントになっているよう\nに関数sin(cos, tan, etc)は引数としてラジアンで表現した角度を受け取る。\n角度をラジアンに変換するには360 で割り、2π を掛けるとよいから\n>>> degrees = 45\n>>> radians = degrees / 360.0 * 2* math.pi\n>>> math.sin(radians)\n0.70710678118654746\nここでmath.pi の表記はmath モジュールで定義されている変数pi を受け取る。\nこの変数の値はπ の15 桁程度の精度の近似値である。三角関数の知識があるとこ\nの結果を2 の平方根を2 で割ったもので確認できる:\n>>> math.sqrt(2)/2.0\n0.70710678118654757\n3.4\n混合計算\nこれまで変数、表式、文などのプログラムの構成要素を単独で、それらを混合\nする使い方の話なしで、眺めてきた。\nプログラミングの強力な特徴の一つはこれらの構成要素を混合(composition)\nして使えることである。例えば、関数の引数には算術演算子を含む任意の表式が\n使える:\n>>> x = math.sin(degrees / 360 * 2 * math.pi)\n関数の呼び出しさえ構わない:\n>>> x = math.exp(math.log(x + 1))\n'}, {'id': '704e3cbb-1d92-4135-89e2-4914eb0a16a9', 'page': 40, 'text': '40\n第3 章\n関数\n一つの例外を除けば、どの場所に値を置いてもよいし、任意の表式を置いてもよ\nい。その例外とは代入文の左辺は一つの変数名でなければならないということだ。\n\u3000左辺に変数名以外の表式を置くと構文エラーになる(この規則の例外は後でみ\nる)\n。だから\n>>> minutes = hours * 60\n#right\n>>> hours * 60 = minutes\n#間違い\nSyntaxError: can’t assign to operator\n3.5\n新規関数の追加\nこれまではpython が提供している関数のみを使ってきたが、新たな関数を作成\nすることもできる。関数定義(function definition)はその新しい関数に名前を\n付けること、関数が呼ばれときに実行される一連の文を定義することである。\n一例を示す:\ndef print_lyrics():\nprint "I’m a lumberjack, and I’m okay."\nprint "I sleep all night and I work all day."\n#(訳注:モンティパイソン「空飛ぶサーカス」より)\ndef は予約語で、これが関数定義であることを示す。関数名はprint lyrics であ\nる。関数名に対する規則は変数名に対するものと同一である。変数名と関数名が\n同じというのは避けるべきだ。関数名の後の空の括弧はこの関数は何も引数を受\nけ取らないことを示す。\n関数の一行目は関数のヘッダー(header)と言われ、残りはボディー(body)\nと言われる。ヘッダーはコロンで終わり、ボディーはインデント(字下げ)される。\n習慣として、このインデントの量は4(3.14 節を参照のこと)である。ボディー\nは任意の数の文を含んで構わない。\nprint 文にある文字列は二重引用符で括られている。シングル引用符と二重引\n用符は同じ作用をもたらすが、一般にシングル引用符が使われる。例外は文字列\nの中に既にシングル引用符(またはアポストロフィ)が使われている場合である。\n関数定義をインタラクティブ・モードで行っているときには、関数定義がまだ\n終わっていないことを知らせる省略記号(...) が表示される:\n>>> def print_lyrics():\n… print "I’m a lumberjack, and I’m okay."\n… print "I sleep all night and I work all day."\n…\n'}, {'id': '3745f756-83da-4ca2-a04d-a033f61ecfeb', 'page': 41, 'text': '3.6.\n関数定義とその利用法\n41\n関数定義を終わりにしたいときには空行を入力する(これはスクリプト・モー\nドでは不必要)\n。関数の定義は同じ名前の変数を生成する:\n>>> print print_lyrics\n\n>>> type(print_lyrics)\n\nつまり、\nprint lyricsの値はbf 関数オブジェクト\n(function object)\nで、\nfunction\n型を持つ。\n関数の呼び出しは組み込み関数と同じである:\n>>> print_lyrics()\nI’m a lumberjack, and I’m okay.\nI sleep all night and I work all day.\n一度関数が定義されると、その関数は他の関数の内部で呼ぶことができる。例\nとして、前の歌詞を繰り返し表示するrepeat lyrics を作ってみる:\n>>> def repeat_lyrics():\n... print_lyrics()\n... print_lyrics()\n...\nそして関数repeat lyrics を呼び出してみる:\n>>> repeat_lyrics()\nI’m a lumberjack, and I’m okay.\nI sleep all night and I work all day.\nI’m a lumberjack, and I’m okay.\nI sleep all night and I work all day.\nこの歌の実際の歌詞はこうではないが。\n3.6\n関数定義とその利用法\n前節で紹介したプログラムを纏めると以下のようになる(スクリプト・モード\nで作成。インデントはtab キーを使うと便利)\n:\n'}, {'id': 'b640acf4-f147-43f3-8c05-9e72f867ee51', 'page': 42, 'text': '42\n第3 章\n関数\ndef print_lyrics():\nprint "I’m a lumberjack, and I’m okay."\nprint "I sleep all night and I work all day."\ndef repeat_lyrics():\nprint_lyrics()\nprint_lyrics()\nrepeat_lyrics()\nこのプログラムは二つの関数定義、print lyrics とrepeat lyrics、を含んでい\nる。関数定義の実行は他の文と同じように行われるが、これは関数オブジェクトを\n生成する作業である。関数が呼ばれるまで関数内部の文の実行は行われない。そ\nして、関数定義自体は何も出力しない。\n予想したように関数の生成はそれが実行される前になされなければならない。別\nな言葉で言うと、関数はそれが呼ばれる前にその関数定義が実行されなければな\nらないのだ。\n練習問題3.1 \u3000この節で紹介したスクリプトで最後の行を最初に持ってきたスク\nリプトに変更して実行してみる。どのようなメッセージがでるか?\n練習問題3.2 \u3000同様に関数定義のrepeat lyrics をprint lyrics の前で行うよ\nうにスクリプトを変更し、実行してみる。何が起こるか?\n3.7\n実行の流れ\n関数はそれが最初に呼ばれる前に定義されなければならないということを確認\nするために、どの文から実行されるかという実行の流れ(flow of execution)を\n知らなければならない。\n実行は常にプログラムの最初からだ。文は一度に一つずつ先頭から末尾に向かっ\nて実行される。\n関数定義の実行もこの流れに沿って実行される。しかし、関数内部の文の実行\nはその関数が呼ばれるまで実行されない。\n関数を呼ぶという文は実行の流れにできた回り道のようなものだ。次ぎの文が\n実行される替わりにそこではその関数のボディーにある文が実行され、そして別\nれたところに戻ってくる。\n'}, {'id': '2f4e9a7c-b797-4556-90b4-402e0af3232d', 'page': 43, 'text': '3.8.\n仮引数と実引数\n43\n呼ばれた関数がまた別な関数を呼ぶことができると言う事態を想定しなければ、\n話は簡単だ。一つの関数の途中で他の関数の文を実行しなければならないかもし\nれない。また、この新たな関数が他の関数を呼ぶということもあり得る。\n幸にしてPython は実行状況を詳しく追跡することに優秀であるので、関数内の\n文が実行完了するとその関数を呼んだ関数内でこの関数に実行が移ったところに\n戻ってくる。プログラムの最後に来ると実行は終了になる。\nこの「不道徳なお話」の寓意は何であろうか?プログラムを常に上から下へと\n読む必要はなく、ときには実行の流れに沿って読む方が賢明なこともあるのだと\nいうことだ。\n3.8\n仮引数と実引数\nこれまでみてきた組み込み関数のいくつかは引数を要求したものである。例え\nば、|math.sin は一つの数を引数として受け取る。いくつかの関数は一つ以上の\n引数を受け取る。math.pow は指数計算で底と指数の二つを受け取る。\n関数の中では、\nその引数は仮引数\n(parameters)と呼ばれる変数に代入される。\n以下は引数を一つ受け取る新たに定義した関数の例である:\ndef print_twice(bruce):\nprint bruce\nprint bruce\nこの関数は引数をbruce と名前である仮引数に代入する。この関数が呼ばれると、\n仮引数の値をそれが何であれ)二回print する。\nこの関数の引数は何であれprint できるものであれば有効である:\n>>> print_twice(’Spam’)\nSpam\nSpam\n>>> print_twice(17)\n17\n17\n>>> print_twice(math.pi)\n3.14159265359\n3.14159265359\n組み込み関数に適用できた混合計算の規則は新たに定義された関数にも適用さ\nれる。従って、print twice 関数の引数には任意の表記を書くことができる:\n'}, {'id': 'fbef3082-3507-4c7a-96e2-a2d7dfb3337c', 'page': 44, 'text': '44\n第3 章\n関数\n>>> print_twice(’Spam’ * 4)\nSpamSpamSpamSpam\nSpamSpamSpamSpam\n>>> print_twice(math.cos(math.pi))\n-1.0\n-1.0\n引数は関数が呼ばれる前に評価されるので、上記の二例の引数、’Spam’ * 4 や\nmath.cos(math.pi) は一度だけ評価されるだけである。\n勿論、引数は一つの変数でもよい:\n>>> michael = ’Eric, the half a bee.’\n>>> print_twice(michael)\nEric, the half a bee.\nEric, the half a bee.\nこの関数で我々が引き渡す引数の名前michael と関数の仮引数の名前bruce とは\n何の関係もない。この関数を呼んだプログラムの中でその値が何と呼ばれていた\nかということには無関係で、print twice では全てがbruce と呼ばれる。\n3.9\n変数や仮引数はローカルである\n関数の内部で変数を生成したときには、この変数はローカル変数(local)で関\n数の内部でしか存在しない。例を示す:\ndef cat_twice(part1, part2):\ncat = part1 + part2\nprint_twice(cat)\nこの関数は二つの引数を受け取り、それらを連結し二回print する。これを使って\nみる:\n>>> line1 = ’Bing tiddle ’\n>>> line2 = ’tiddle bang. ’\n>>> cat_twice(line1, line2)\nBing tiddle tiddle bang.\nBing tiddle tiddle bang.\n#(訳注:モンティパイソン「空飛ぶサーカス」より)\n'}, {'id': 'a4803386-eeff-4cac-b5bb-d20b07435f88', 'page': 45, 'text': '3.10.\nスタック図\n45\n関数cat twice が終了した後、変数cat は消滅する。たからこの変数をprint し\nようとすると例外になる:\n>>> print cat\nNameError: name ’cat’ is not defined\n仮引数もローカル変数である。例えば、関数print twice の外部ではbruce と\nいうものは存在しない。\n3.10\nスタック図\nどの変数がどこで使われているかを追跡するために、スタック図(fig.stack)を\n書いてみるのも有用だ。状態図と同じように、スタック図も各変数の値を表示す\nるが、どの関数に属するかも示す。\n各変数はフレーム(frame)によって区切られる。フレームはその脇に関数名を\n付した矩形枠で内部ではその関数の仮引数や変数の名前がある。先の例のスタッ\nク図は図3.1 に示した。\nフレームはどの関数がどの関数を呼んだかということが分かるように積み重ね\nた状態で表現されている。われわれの例では、print twice はcat twice によっ\nて呼ばれているし、cat twice は特別な最上位のフレーム、__main__によって呼\nばれている。関数の外側で変数を生成すると、この変数は__main__に属すること\nになる。各仮引数は対応する引数が持つ値と同じものだ。だから、part1 はline1\nline1\nline2\n’tiddle bang.’\npart1\npart2\ncat\nbruce\n’Bing tiddle ’\n’Bing tiddle ’\n’tiddle bang.’\n’Bing tiddle tiddle bang.’\n’Bing tiddle tiddle bang.’\n\ncat_twice\nprint_twice\n図3.1: スタック図.\nと同じ値を参照しているし、line2 も然りである。bruce はcat と同じ値を持つこ\nとになる。\n関数呼び出しのときにエラーを起こすと、Python はその関数の名前、関数を呼\nんだ関数の名前、その前というように__main__に至る全ての関数の名前を表示す\n'}, {'id': '179dd5b6-7e00-4102-9e95-458e15e23f69', 'page': 46, 'text': '46\n第3 章\n関数\nる。例えば、関数print twice 内でcat にアクセスしようとすると、NameError\nになる:\nTraceback (most recent call last):\nFile "test.py", line 11, in \ncat_twice(’bono’, ’niko’)\nFile "test.py", line 8, in cat_twice\nprint_twice(cat)\nFile "test.py", line 4, in print_twice\nprint cat\nNameError: global name ’cat’ is not defined\nこの関数のリストはトレースバック(traceback)と呼ばれるものである。これは\nプログラムのどこでこのエラーが発生したか、そして如何なる関数を実行してい\nるときかといったことを示す。さらにこのエラーが起きたソースコードの行番号\nも示される。このトレースバックに現れる関数はスタック図と同じ順序で示され\nる。現在実行中の関数は一番下になる。\n(訳注:上のトレースバックを発生させたスクリプトを以下に示す。ファイル名\nはtest.py:\n01 def print_twice(bruce):\n02\nprint bruce\n03\nprint bruce\n04\nprint cat #誤り\n05\n06 def cat_twice(line1, line2):\n07\ncat = line1 + line2\n08\nprint_twice(cat)\n09\n10 #main\n11 cat_twice(’bono’, ’niko’)\n左端の行番号は便宜的に付けたものである。上のトレースバックはまずスクリプ\nトtest.py の11 行目のメイン(module)でcat twice(’bono’, ’niko’) が実行\nされ、次に8 行の関数cat twice 内のprint twice(cat) が実行され、次に4 行目\nの関数print twice 内のprint cat でエラーが検出されたことを知らせている)\n'}, {'id': '1c8a3dc3-1984-47f5-96d7-ba3cd709fea7', 'page': 47, 'text': '3.11.\n結果を生む関数とボイド関数\n47\n3.11\n結果を生む関数とボイド関数\nわれわれが使っている関数、特に数学関数は結果を生む関数である。適当な名\n前がないのでこれを結果を生む関数(fruitful function)と名付けることにする。\nprint twice のような関数は実行されても結果を返さないので、これはボイド関\n数(void function)と呼ばれている。\n関数を呼ぶというときには、何時も何か返すものを伴う実行がなされると期待\nするものである。だからその結果をある変数に代入、表式の一部として使う。\nx = math.cos(radians)\ngolden = (math.sqrt(5)+1)/2\n関数をインタラクティブ・モードで使うと、Python は結果を表示する:\n>>> math.sqrt(5)\n2.2360679774997898\nところが、スクリプト・モードで結果を生む関数をそれ自体で呼ぶと、結果は永\n遠に失われてしまう:\nmath.sqrt(5)\nスクリプトは5の平方根を計算するが、保存し、表示する機能を持っていないの\nで不便なことになる。\nボイド関数も画面に表示をし、似た効果をもたらすが戻り値がない。その結果\nを変数で受け取ると、その値はNone という特別な値になる:\n>>> result = print_twice(’Bing’)\nBing\nBing\n>>> print result\nNone\n値None は文字列’None’ とは異なる。それはそれ自身の型をもつ特別な値である:\n>>> print type(None)\n\nこれまで作成した関数は全てボイド関数であった。これからの数章では結果を生\nむ関数を書くことになる。\n'}, {'id': '3638596e-8f02-4b1b-a931-1caf924de058', 'page': 48, 'text': '48第3章関数3.12なぜ関数?プログラムを関数に分割することに心を砕く理由はあまりはっきりとしないかもしれない。その理由としていくつかある。•関数を生成することで一括りの文に名前を付けることができ、読みやすく、デバッグし易いプログラムを作ることができる。•関数化することで繰り返しをなくし、プログラムを短くすることができる。そして、何か変更が必要なことが起きたときには一個所を修正することで足りる。•長いプログラムを関数に分割することで関数ごとのデバッグが可能になり、その後にそれらを全体して纏めることができる。•良くできた関数は多くにプログラムで有用だ。一度書いてデバッグして完成させると、それを再利用することができる。3.13from付きのインポートPyhtonは二つの方法でモジュールをインポートする方法を提供している。その一つは既に出会った:>>>importmath>>>printmath>>>printmath.pi3.14159265359mathをインポートするとmathという名前のオブジェクトは生成される。このモジュールにはpiのような定数やsin、expといった関数が含まれている。しかし、直接piにアクセスしようとするとエラーになる:>>>printpiTraceback(mostrecentcalllast):File"",line1,inprintpiNameError:name’pi’isnotdefinedモジュールのインポートするもう一つの方法は以下のようなものだ:\x0c'}, {'id': 'd7253150-3319-449f-b4ba-e2b778ef215f', 'page': 49, 'text': '3.14.デバッギング49>>>frommathimportpiこの場合にはドット表記なしに直接piにアクセスできる:>>>printpi3.14159265359また、モジュールの全てはスター演算子、*でインポートできる:>>>frommathimport*>>>cos(pi)-1.0このインポートの方法はソースコードをよりコンパクトにできる長所があるが、ソースコードで使っている変数名とモジュールで定義されている名前が競合する短所もある。3.14デバッギングもしテキストエディタを使ってプログラムを書いているのであれば、空白とタブのどちらを使うべきか悩むであろう。最良はタブなして空白のみを使うことだ。多くのテキストエディタはPythonが行うべきことをそのままで知っているはずだが、そうでないものもある。タブも空白も見えないので厄介だ。桁下げをやってくれるエディタをみつけるようにしてほしい。また、プログラムを実行する前に保存することを忘れないように。多くの開発環境では自動保存の機能が備わっているが、そうでないものもある。この場合にはエディタで眺めているプログラムと実行したプログラムが異なっているということもあり得る。デバッギングに際して、同一の間違ったプログラムを走らせ続けていたら、途方もない時間の無駄だ。自分が眺めているソースコードが実行させているプログラムだということを確認しよう。確認できないときは、プログラムの先頭にprint’hello’などの文を置き、再実行させてみる。もしもhelloが見えないとすると、それは正しいプログラムが走っていない証拠だ。\x0c'}, {'id': '79872ab7-ce0a-406c-a238-0e0f7fbcbee4', 'page': 50, 'text': '50第3章関数3.15語句関数(function):意味のある命令を実行する文の纏まりに名前を付けたもの。関数は引数を取るものもあり、結果を返すものもある。関数定義(functiondefinition):関数名、仮引数、実行する文の纏まりを指定して新たな関数を作成するための文。関数オブジェクト(functionobject):関数定義で生成される一つの値。関数名はこの関数オブジェクトを参照する変数である。ヘッダー(header):関数定義の一行目の部分。.:ボディー(body):関数定義の内部の本体(文の集合)。仮引数(parameters):関数の中で引数として渡される値を参照する変数。関数呼び出し(functioncall):関数を実行する文。関数名と必要な実引数のリストが続く。引数(argument):関数が呼ばれたときその関数に付随して与えられる値。この値は関数の中では対応する仮引数に代入される。ローカル変数(local):関数内で定義され使われる変数。ローカル変数はその関数内でのみ有効である。戻り値(returnvalue):関数の結果。もし関数が表式として使われているとすると、その戻り値がその表式の値となる。結果を生む関数(fruitfulfunction):結果を返す関数。ボイド関数(voidfunction):値を返さない関数。モジュール(module):関連する関数群や付随する定義を一つに纏めたファイル。インポート文(importstatement):一つのモジュールを読み込みモジュール・オブジェクトを生成する文。モジュール・オブジェクト(moduleobject):一つのモジュール内で定義されている値へのアクセスを提供するimport文が生成する値。\x0c'}, {'id': 'f1884ba5-87b4-4413-9d9c-fcb28b0bccc8', 'page': 51, 'text': '3.16.\n練習問題\n51\nドット表記(dot notation)\n:他のモジュールに属する関数や定義を使うとき、そ\nれらをモジュール名に続いてピリオドそして関数名(または定義変数)で指\n定する構文規則。\n混合(composition)\n:より長い表式の一部として表式を、またはより複雑な文の\n一部として文を使うこと。\n実行の流れ(flow of execution)\n:プログラムの実行時にどの文から実行される\nかの順序。.\nスタック図(fig.stack)\n:一つのモジュールで使われる関数群やそれらの内部で定\n義される変数、そしてそれらの変数が参照する値の対応を表示するグラフィ\nカルな表現。\nフレーム(frame)\n:スタック図内で関数呼び出しを表現する枠。\nトレースバック(traceback)\n:例外が発生したときに表示される実行されていた\n関数のリスト。\n3.16\n練習問題\n練習問題3.3\u3000Pythonは文字列の長さを返す組み込み関数len、\n例えばlen(’allen’)\nは5 を返す、を提供している。\n文字列の表示で、文字列の最後の文字が70 桁にくるようなright justify 関数\nを作成せよ。\n>>> right_justify(’allen’)\nallen\n練習問題3.4 \u3000関数オブジェクト自体も関数の引数とすることができる。例えば、\ndo twice 関数は関数オブジェクトを引数として受け取りその関数を二度実行する、\nつまり\ndef do_twice(f):\nf()\nf()\nさて、do twice が以下の関数print spam を引数とする使用例をみてみよう:\n'}, {'id': 'd2db27fe-6133-4d9b-a952-e5ee8ae2e71b', 'page': 52, 'text': '52\n第3 章\n関数\ndef print_spam():\nprint ’spam’\ndo_twice(print_spam)\nこの関数を以下の順序を踏んで変更せよ:\n1. 上記のプログラムをスクリプトとして書き、実行せよ。\n2. 引数を二つ持つようにdo twice を変更せよ。一つの引数は関数オブジェク\nトであり、他はこの関数が受け取る引数の値である。\n3. print spam をより一般的な関数print twice に変更せよ。この新しい関数\nは引数を一つ持ち、それを二回表示する。\n4. 新しい関数do four を作成せよ。この関数は関数オブジェクトを引数の一つ\nとし、他の引数はこの関数が引数として受け取る値である。この新しい関数\nは引数として受け取った関数を四回実行する。この新しい関数の本体は二つ\nの文で書ける。四つではないよ。\n解答例:http://thinpython.com/code/do_four.py\n練習問題3.5 \u3000この練習問題はこれまで学んだ文や他の機能を使って解答できる\nものである。\n1.以下のような格子を描く関数を作成せよ。\n+ - - - - + - - - - +\n|\n|\n|\n|\n|\n|\n|\n|\n|\n|\n|\n|\n+ - - - - + - - - - +\n|\n|\n|\n|\n|\n|\n|\n|\n|\n|\n|\n|\n+ - - - - + - - - - +\nヒント:print 文で一つ以上の値を一時に表示したいときは、値をカンマで区切っ\nたものでprint とするとよい:\n'}, {'id': 'a6da5514-a726-495f-8853-9bd77c7c62fb', 'page': 53, 'text': '3.16.練習問題53print’+’,’-’そして、もしprint文をカンマで終わりにすると、Pythonはprint文が完了してないとみなし、次ぎのprint文も同一の行に表示する:print’+’,print’-’この出力は’+-’である。print文それ自体で行が終わったら、改行で次ぎに行に移る。2.同様に四行四列の格子を描く関数を作成せよ。解答例:http://thinpython.com/code/grid.py。出典:この練習問題はOualline著”PracticalProgramming.ThirdEdition(O’RellyMedia,1997)”の中の練習問題を基礎とした。\x0c'}, {'id': '98f96faa-eb64-472d-9e27-0291d0c3aeeb', 'page': 1, 'text': ''}, {'id': '70df8e3a-6ad5-46c3-8442-f4a7922bf658', 'page': 55, 'text': '55第4章事例研究:インタフェース設計この章のサンプルコードはhttp://thinkpython.com/code/polygon.pyにある。4.1カメの世界この本の付録としてSwampyというパッケージ(package)を私は作った。このパッケージはhttp://thinkpython.com/code/swampyからダウンロードできる。パッケージは多くのモジュールを集めてものである。このSwampyに集録したモジュールの一つにTurtleWorldがある。これは画面をカメが這い回るようにして線の描画をする多くの関数を纏めたものである。このパッケージをインストールしてあると、このTurtleWorldをインポートできる:fromswampy.TurtleWorldimport*Swampyはダウンロードしたけれどパッケージとしてインストールしないのであれば、Swampyファイル類が存在するディレクトリで作業をするか、またはPythonの検索パスにこのディレクトリを追加する方法を採ってほしい。この場合にはfromTurtleWorldimport*でTurtleWorldをインポートできる。ダウンロードの過程やPythonの検索パスの話はあなたが使っているシステムに依存するので、ここでは細部は触れない。いくつかのシステムの状況についてはhttp://thinkpython.com/swampyに随時情報を掲載するつもりである。さて、mypolygon.pyという名前のファイルを作り、以下のコードを作成してみよう:fromswampy.TurtleWorldimport*world=TurtleWorld()\x0c'}, {'id': 'a583e63e-dd53-4a56-a69e-0a2f582f0535', 'page': 56, 'text': '56\n第4 章\n事例研究:インタフェース設計\nbob = Turtle()\nprint bob\nwait_for_user()\n第一行でswampy パッケージの中にあるTurtleWorld モジュールの全てをインポー\nトしている。引き続く行では、world にTurtleWorld を代入、bob にTurtle を代\n入している。print bob で以下のような表示が出るはずである。\n\nこれはbobがTurtleWorldモジュールのクラスTurtleのインスタンス\n(instance)\nを参照していることを意味している。つまり、bob はTurtle のメンバーの一つで\nあることを示している。\nwait for users はユーザが何か応答するまで、ユーザはこの場合は窓を閉じる\n以外にすることがないが、TurtleWorld が待ちの状態にあることを示す。\nモジュールTurtleWorld はturtle を操縦するためのいくつかの関数を提供し\nている。fd とbk は前進と後退、lt とrt は左転回と右転回である。各turtle は上\nげ下げできるペンを持っていて、ペンが下がっているときには、その軌跡は線と\nなって残る。pu とpd はペンの「上げ」と「下げ」のつもりだ。\n直角辺を描くために、以下の行を追加してみよう(追加する場所はbob の生成\nの後で、wait for user の前である)\n。\nfd(bob, 100)\nlt(bob)\nfd(bob, 100)\n第一行ではbob は100 だけ前進、第二行では左転回する。このプログラムを実行\nすると、bob は東に向いて前進し、その後北向きに進路を取る。\nこのプログラムを変更して、正方形を描いてみよう。上手く行くまで続けてみ\nよう。\n4.2\n簡単な繰り返し\n多分あなたが行った変更は以下のようなものはずだ(ただしTutleWorld の生成\nとwait for user を除外して)\n:\nfd(bob, 100)\nlt(bob)\n'}, {'id': '0c5a7585-e50c-4e7a-81b6-4e824825375b', 'page': 57, 'text': '4.3.練習問題57fd(bob,100)lt(bob)fd(bob,100)lt(bob)fd(bob,100)これらをmypolygon.pyに追加して実行してみよう。これはfor文を使うともっとコンパクトに書ける:foriinrange(4):print’hello!’得られる結果は以下である:hello!hello!hello!hello!これは簡単なfor文の利用法である。詳細は後にみることにする。正方形を描くプログラムにfor文を使うのには、上の例題で充分だ。以下は正方形描画のfor文版だ。foriinrange(4):fd(bob,100)lt(bob)for文の構文は関数定義と似ている。コロンで終わるヘッダーを持ち、インデントされたボディからなる。ボディは任意の数の文を含んで構わない。for文はループ(loop)と呼ばれる。実行の流れがボディを実行してあとボディのトップに戻るからである。今の場合はボディを4回繰り返す。このfor文を使った正方形描画プログラムはオリジナルのものとホンの僅かだが異なっている。それは正方形を描いた後、bobが余計な左転回をすることだ。4.3練習問題以下はTurtleWorldを使った練習問題のシリーズである。それらは面白いものであるが、学習のポイントも含まれている。作業をしながらその学習ポイントにも目を向けてほしい。以下の節でその解答を提示するが、完成するまで(少なくも試みるまで)はそれをみてはいけない。\x0c'}, {'id': '5e98e424-e24b-4cb3-977e-565537460ffd', 'page': 58, 'text': '58第4章事例研究:インタフェース設計1.squareという関数を作成せよ。仮引数としてturtleであるtを持つ。このturtleで正方形を描くようにせよ。そしてこの関数でbobを引数として受け取り関数呼び出しを書き実行せよ。2.lengthという別な仮引数を追加せよ。関数のボディを変更してlengthを一辺の長さにするように変更し、関数呼び出しに第二の引数を持つように変更せよ。適当な長さのlengthを与え実行してみよう。3.関数ltの回転角度の既定値は90である。しかし、lt(bob,45)のように角度を与えることもできる。そこでsquareをコピーして名前をpolygonとする。この関数は第二の仮引数nを持ち、ボディをn個の辺を持つ正多角形を描くように変更せよ。ヒント:n個の辺を持つ正多角形の外角は360/nである。4.circle関数を作成せよ。この関数はpolygon関数から派生し、仮引数としてturtleを値に持つtと、半径rを持ち、近似的に円を描く関数である。ヒント:円周は近似的に正n辺多角形で近似すると、円周\u3000=length*nで表現できる。ここでlengthは多角形の一辺の長さである。もう一つヒント:描画速度が余りのも遅いときは、bob,delay=0.01とするとよい。5.更に一般的な関数arc関数を作成せよ。この関数は追加の仮引数としてangleを持つ。描画はこの(cid:3)angleを角度とする孤を描くことにある。angle=360で完全な円を描くことになる。4.4カプセル化第一例は正方形を描画するコードを関数にする問題である。解答例はdefsquare(t):foriinrange(4):fd(t,100)lt(t)square(bob)\x0c'}, {'id': '2d9ae053-c968-4a95-87c4-fc4855e8fcb7', 'page': 59, 'text': '4.5.一般化59最も内側のfdとltは二段にインデントされる。これらは関数定義のボディの始まりであるforループの内部にある。次の行にあるsquare(bob)は左余白をなくして入力される。このことはforループも関数定義も終了したことを意味する。関数の内部ではtは同一のturtleであるbobを参照しているから、lt(t)はlt(bob)と同じ効果を持つ。それならば、何故直接bobを呼ばないのか?これは関数squareではtはどんなturtleにもなり得るからだ。二匹目のturtleを作成し、それをこの関数の引数にすることもできる、つまり:ray=Turtle()square(ray)コードの一部を関数として纏める作業をカプセル化(encapsulation)と呼ぶ。カプセル化の利点は関数名としてコードに名前がつくのでドキュメントとなること。もう一つの利点はコードを再利用したいとき、関数呼び出しで事足りることである。4.5一般化次ぎのステップはsquare関数に仮引数lengthを追加することである。解答例はdefsquare(t,lenght):foriinrange(4):fd(t,lenght)lt(t)square(bob,200)関数に仮引数を追加する作業を一般化(generalization)という。この作業で関数はさらに一般化される。次ぎのステップも一般化で、正方形を描画する替わりに、正多角形を描画するものである。解答例は以下である:defpolygon(t,lenght,n):angle=360/nforiinrange(n):fd(t,lenght)lt(t,angle)polygon(bob,7,70)\x0c'}, {'id': '4ecab570-c033-41e3-bdb8-f74f6b5cd3fc', 'page': 60, 'text': '60第4章事例研究:インタフェース設計7辺を持ち、一辺の長さが70の正多角形を描画する。もし引数の数が増えて、その数値の意味が曖昧になり、順序が不明になったときは、仮引数の名前を付して引数を書くことは合法である:polygon(bob,n=7,length=70)これはキー付き引数(keywordargument)という。この構文規則はプログラムをより読みやすくする。4.6インタフェース設計次ぎのステップは半径rを仮引数として円を描画する関数の作成の問題である。以下は50辺を持つ多角形を描画する解答の一例である:defcircle(t,r):circumferance=2*math.pi*rn=50lenght=circumferance/npolygon(t,lenght,n)第一行では円の円周を計算している。πの値は数学モジュールで与えられるので、mathモジュールのインポートを忘れないように。nは多角形の辺の数であり、この多角形で半径rの円を近似的に描画することができることを使う。この解答例の問題点はnが定数であることである。これでは大きな円では一辺の長さが大きくなって円の近似としては粗くなる。逆に、小さな円では一辺が小さくなりすぎて描画時間が無駄になる。nを仮引数にすることも一案だが、インタフェースとしては明確さを欠くことにある。関数のインタフェース(inteface)とはその関数の使い方の指針である:「仮引数は何か?」、「この関数は何をする関数か?」、「戻り値は何か?」について纏めたものである。そのインタフェースは明確であるということは、「可能なかぎり簡単で、簡単すぎることもない」(アインシュタイン)ことである。今の例では、rは描画しようとする円の半径の値を特定するものであるのでインタフェースに属すると考えられる。しかし、nは如何に円を描画するかに使うものなので、そうではない。これはインタフェースを乱雑にするだけなので、むしろ関数のボディのなかで円周から計算する方がよい。\x0c'}, {'id': 'af9da3d7-28be-4f42-b2f4-b953b970592e', 'page': 61, 'text': '4.7.再因子分解61defcircle(t,r):circumferance=2*math.pi*rn=int(circumferance/3)+1lenght=circumferance/npolygon(t,lenght,n)これで一辺の数は—ttcircumferance/3になり、一辺の長さは約3になる。これはできる多角形で充分に円を近似できる最小で、しかも大きな円でも充分な数である。これで如何なる大きさの円の描画に使える関数ができたことになる。4.7再因子分解円の描画では関数polygonを再利用できた。これは正多角形が円の近似になるからである。しかし、円弧を描画しようとするとpolygonもcircleも使えない。一つの解決方法は関数polygonのソースコードをコピーして、直接に関数arcに加工するものである。結果は以下のようになる:defarc(t,r,angle):arc_lenght=2*math.pi*r*angle/360n=int(arc_lenght/3)+1step_lenght=arc_lenght/nstep_angle=float(angle)/nforiinrange(n):fd(t,step_lenght)lt(t,step_angle)後半は関数polygonに似た処理をしているが、polygonを書き換えないと使えない。そこで、polygonを第三の引数を持たせるように書き換えることにする。名前もpolylineとする:defpolyline(t,n,lenght,angle):foriinrange(n):fd(t,lenght)lt(t,angle)この新しく定義された関数はpolygonにも使えるし、arcにも使える。polygonは以下のようになる:\x0c'}, {'id': 'f8162999-9ada-4dc4-be4e-7a443a51b819', 'page': 62, 'text': '62第4章事例研究:インタフェース設計defpolygon(t,n,lenght):angle=360.0/npolyline(t,n,lenght,angle)関数arcは以下のようになる:defarc(t,r,angle):arc_lenght=2*math.pi*r*angle/360n=int(arc_lenght/3)+1step_lenght=arc_lenght/nstep_angle=float(angle)/npolyline(t,n,step_lenght,step_angle)円周は円弧の特別な形なので、関数circleは関数arcで書くことができる:defcircle(t,r):arc(t,r,360)以上のような過程、つまり関数インタフェースを改良し、コードの再利用を促進する方法を再因子分解(refactoring)という。いまの場合は関数arcと関数polygonとに共通する部分があり、この共通項として関数polylineを抽出したことになる。最初からこのことが分かっていたら、関数ttpolylineから始めたかもしれない。しかし、多くの場合プロジェクトの最初から全ての関数インタフェースを設計しておくことは難しい。プログラムを書き始めて問題が良く理解できるものである。ときとして再因子分解をしようと思うことは問題をよりよく理解できたサインである。4.8開発計画開発計画(developmentplan)はプログラムを書く過程のことである。この過程としてわれわれがみてきたものは、「カプセル化と一般化」である。この過程は以下のように纏められる:1.まず、関数を使わないで小さいプログラムを書く。2.プログラムは実行できるようになったら、それを関数としてカプセル化し、名前を付ける。3.適宜仮引数を追加してそれを一般化する。\x0c'}, {'id': 'f2f393b0-7a21-48b6-9bf2-38139b4d1f28', 'page': 63, 'text': '4.9.ドキュメント文字列634.1〜3を繰り返し、プログラムが動くようにする。5.再因子分解でプログラムが改良できるかコードを眺めてみる。例えば、プログラムの中に同じようなコードを使っている個所あれば、適当な関数でそれらを置き換える。この過程は欠点もあるが、もしプログラムをどのような関数に分解したらよいか事前に分からないときには助けになる(後に別なアプローチを示す)。この方法はプログラミングを進めながら設計も進める方法である。4.9ドキュメント文字列ドキュメント文字列(docstring)とは関数のインタフェースを説明するために関数の初めの部分に置く文字列のことである。例を示す。defpolyline(t,n,lenght,angle):"""与えられた長さ(lenght)と与えられた線分間角度(angle)(度)を持つn個の線分を描画する。tはturtleである。"""foriinrange(n):fd(t,lenght)lt(t,angle)この文字列は三重の引用符で括られている。三重の引用符は複数行に渡る文字列を書くことができるからだ。この例のようにドキュメント文字列は短いがこの関数を使おうとしたときに必要な情報を含んでいる。これは簡潔にこの関数の機能が関数の細部に渡らずに説明されているし、仮引数の役割(時には型についても)も示されている。このようなドキュメントを書くことは関数のインタフェースの設計の重要な部分である。良く設計されたインタフェースは説明も明快にできるはずである。もし関数のインタフェースの説明に苦労するようであれば、それは多分にインタフェース自体の改良が必要なサインであり得る。4.10デバッギングインタフェースは関数とその呼び手との接面のようなものである。呼び手は仮引数に対して値を与えることに同意し、関数はそれをもとに作業をすることに同意する。\x0c'}, {'id': '757a87e3-5155-4a93-bd3b-89cb5cbb37be', 'page': 64, 'text': '64第4章事例研究:インタフェース設計例としてみると、polylineは四個の引数を要求している。tはturtle、nは線分の数、従って整数、lengthは線分の長さ、だから正の数値、angleは度を単位とする数値である。これらの要求は事前条件(precondition)という。関数が実行される前に実現しておく必要のある条件だからである。この逆に、関数の終わりで示される条件は事後条件(postcondition)と言われるものである。これにはその関数が意図したもの(線分の描画のような)、その関数の実行による副産物(turtleの移動、世界に及ぼす変更のような)が含まれる。事前条件は呼び手が責任を負うものである。呼び手がこの事前条件に反し、関数が正常に働かないとすると、バグは呼び手にあり、関数ではない。4.11語句インスタンス(instance):一つの集合の一構成員。この章のworldはTurtleWorld(定義)の一構成員(実体)である。ループ(loop):繰り返して実行されるプログラムの部分。カプセル化(encapsulation):一つの目的のための一連の文の集合を一つの関数として変形する過程。.一般化(generalization):必要なく特定されているもの(例えば数)を適当な一般的なもの(変数や仮引数)に置き換える過程。キー付き引数(keywordargument):「キー」として仮引数の変数名を含めた実引数インタフェース(inteface):関数名、引数や戻り値の説明を含めたその関数の使い方に関する叙述。再因子分解(refactoring):関数のインタフェースやコードの質的な向上のため作動しているプログラムを改変する過程。開発計画(developmentplan):プログラムを作成する過程。ドキュメント文字列(docstring):関数定義の中で関数のインタフェースを叙述した文字列。事前条件(precondition):関数を呼ぶ前に呼び手が満たすべき条件。\x0c'}, {'id': 'd709da97-b13b-441b-bfb5-dc466d8fc232', 'page': 65, 'text': '4.12.練習問題65事前条件(precondition):関数の実行が終わった時点でその関数が満たすべき条件。4.12練習問題練習問題4.1\u3000http://thinkpyhton.com/code/polygon.pyをダウンロードし以下の課題を行え。1.関数polygonと関数circleにドキュメント文字列を追加せよ。2.関数circle(bob,radius)の実行に伴うスタック図を作成せよ。手書きでもよいし、コードにprint文で追加してもよい。3.4.7節の関数arcは、円の描画が常に想定した円の外側に沿った線分で近似されているので正確さに欠けるところがある。その結果turtleが停止した位置は正しい位置から僅かにずれる。わたしの解答例ではこの誤差を減ずる工夫がされている。コードを読んで納得できるがみよ。図4.1:タートルの花々練習問題4.2\u3000図4.1のような描画に対応する関数の典型的なセットを作成せよ。解答例:http://thinpython.com/code/flower.py及びhttp://thinkpython.com/code/polygon.pyも必要である。練習問題4.3\u3000図4.2のような描画に対応する関数の典型的なセットを作成せよ。解答例:http://thinpython.com/code/pie.py図4.2:タートルのパイ\x0c'}, {'id': '74585b10-7eb0-4701-9bb6-4ff6b37fd9da', 'page': 66, 'text': '66\n第4 章\n事例研究:インタフェース設計\n練習問題4.4 \u3000アルファベットの文字は適当な数の基本要素、つまり横線、縦線、\nいくつかの曲線から生成できる。フォントをこのような要素から作れるようデザ\nインせよ。そして、アルファベットを描画する関数を作成せよ。\n先ず、各一文字を描画する関数を生成する。つまり、draw a、draw b といった\n具合である。これらを纏めてetters.py というファイルにする。あなたの制作し\nた関数をテストするために“turtle typewriter”というモジュールを\nhttp://thinkpython/code/typewriter.py からダウンロードせよ。\n解答例:http://thinkpython.com/code/letters.py\n及びhttp://thinkpython.com/code/polygon.py も必要である。\n練習問題4.5 \u3000渦巻について、http://en.wikipedia.org/wiki/Spiral(訳注:\n日本語ではhttp://ja.wikipedia.org/wiki/渦巻)の記事をよみ、アルキメデス\nの渦巻(または、類似の渦巻)を描画するプログラムを作成せよ。\n解答例:http://thinkpython.com/code/spiral.py\n'}, {'id': 'a4232af7-cad2-4de4-8a64-fcdba68bca89', 'page': 67, 'text': '67第5章条件文と再帰5.1モジュラ演算子モジュラ演算子(modulusoperator)は整数に対する演算子で、最初の被演算子を二番目の被演算子で除算したときの余りを生成するものである。Pythonではこの演算子は%で表現される。構文は他の演算子と同じである:>>>quotient=7/3>>>printquotient2>>>remainder=7%3>>>printremainder17を3で割ると商は2、余り1となる。モジュラ演算子は極めて有用なものであることが分かってくる。例えば、ある整数が他の整数で割り切れるか調べたいときには、x%yがゼロならば、xがyで割り切れるということになる。また、ある整数に一桁目の数字や下何桁かの数字を求めたときにも使える。例えば、x%10とすると、整数xの十進一桁目の数字が、x%100で下二桁の数字が得られる。5.2ブール代数表現ブール代数表現(booleanexpression)は真(true)と偽(false)の表現である。以下の例では、演算子==を使い二つの被演算子を比較し、等しい場合は真をそれ以外は偽を値として持つ。>>>5==5True>>>5==6False\x0c'}, {'id': '9e771234-5860-4218-b859-4385108fc5b9', 'page': 68, 'text': '68第5章条件文と再帰TrueとFalseはbool型に属する特別な値である。これらは文字列ではない:>>>type(True)>>>type(False)演算子==は比較演算子(relationaloperators)の一つであり、他の演算子としてはx!=y#xはyに等しくないx>y#xはyより大きいx=y#xはyより大きいが等しいx<=y#xはyより小さいか等しいこれらの演算子は数学的な記号として馴染み深いものであるが、Pythonでの記号の意味はそれらと若干異なる。もっともよくある間違いは二重等号(==)の替わりに、等号(=)を使ってしまうことだ。記号=は代入演算子であり、記号==が比較演算子である。また、=>や=<はない。5.3論理演算子三つの論理演算子(logicaloperators)、and、or、notがある。これらの意味はその英語の意味に似ている。例えば、x>0andx<10はxが0より大きく、かつ10より小さいとき真の値を持つ。x%2==0orx%3==0はどちらかの条件が満たされると真の値を持つ。つまり、ある数が2か3かで割り切れればよい。最後はnotでブール代数の否定演算子である。not(x>y)は、x>yが偽のとき真、つまりxがyより小さいか等しいときである。厳密に言って、論理演算子の被演算子は論理表式であるべきだが、Pythonはそう厳密ではない。非ゼロの如何なる数値も論理的に真と解釈する。>>>17andTrueTrueこの柔軟性は有用であるが、混乱を引き起こす微妙な違いがある。このような使い方は避けた方がよい(何が起きているか把握できるまでは)。\x0c'}, {'id': '66a3bf9e-8b68-4533-873d-a26258a30ad5', 'page': 69, 'text': '5.4.条件処理695.4条件処理有用なプログラムを書くためにはわれわれは条件を調べ、それに従ってプログラムの振る舞いを替える機能を使う。条件文(conditionalstatement)はそのような機能である。このif文の極簡単な例はifx>0:print’xispositive’ifの後のブール代数表式は条件(condition)と呼ばれる。それが真であると、インデントされた文が実行され、そうでないと実行されない。条件文は関数の定義と同じ構造をしている。つまり、インデントされたボディを従えたヘッダー。このような文を複合文(compoundstatements)という。ボディは無制限の数の文があってもよいが、最低でも一文は必要である。ときとして文なしのボディが必要なときがある(大抵の場合これはまだ書き終えていない部分を余白として残したいときだ)。このようなときは何もしないpass文を使うとよい:ifx<0:pass#負の場合の処理が必要5.5二者選択処理if文の二番目のかたちは二者選択処理(alternativeexecution)である。これでは二つ可能性があり、条件はどっちを取るかを決める。構文は以下のようだ:ifx%2==0:print’xiseven’else:print’xisodd’条件は真または偽のはずなので、二者の内の一つは必ず実行される。二者択一は、実行の流れの分岐であるから、分岐処理(branches)とも言われる。5.6条件文の連鎖ときとして二つ以上の可能性がある場合があり、二つ以上の分岐が必要となる。このような処理を表現する一つの方法は条件文の連鎖(chainedconditional)を使うものがある:\x0c'}, {'id': 'b967bc67-05c5-4657-8749-c3824bce30a6', 'page': 70, 'text': '70第5章条件文と再帰ifxy:print’xisgreaterthany’else:print’xandyareequal’elifは\u3000“elseif”の省略形である。ここでも一つの分枝が必ず実行される。elif文の個数には制限がない。else節を使うときはこれを最後に置く。これがある必要もない。ifchoice==’a’:draw_a()elifchoice==’b’:draw_b()elifchoice==’c’:draw_c()各条件は順次調べられる。もし初めが偽であるとすると、次ぎが調べられという具合である。それらの一つが真であるとその分枝が実行され、文の終わりになる。一つ以上の真があっても初めの真の分枝が実行されるだけである。5.7入れ子の条件処理条件処理は入れ子にできる。三分枝の例題は以下のようになる:ifx==y:print’xandyareequal’else:ifx10:print’xは一桁の整数である’5.8再帰一つの関数が他の関数を呼ぶことは合法であるが、自分自身を呼ぶことも合法だ。このようなことで何か得なことがあるとは俄には信じられないが、これはプログラムで実現できる魔法のような世界であることが徐々に明らかになる。例として以下のようなプログラムを眺めてみよう:defcountdown(n):ifn==0:print’Blastoff!’else:printncountdown(n-1)nが0か負になるとBlastoff!(発射!)’をprintする。それ以外はnを表示し、引数をn-1として関数countdown、つまり自分自身を呼ぶ。main関数でこの関数を以下のように呼んだら何がおきるだろう:>>>countdown(3)関数countdownはn=3から始まる。nの値が0より大きいので、nの値3をprintして自分自身を呼ぶ。関数countdownはn=2で実行される。nの値が0より大きいので、nの値2をprintして自分自身を呼ぶ。関数countdownはn=1で実行される。nの値が0より大きいので、nの値1をprintして自分自身を呼ぶ。\x0c'}, {'id': '2a30d873-d30b-4e00-852e-5e225953a051', 'page': 72, 'text': '72第5章条件文と再帰関数countdownはn=0で実行される。nの値が0に等しいので、”Blastoff!”をprintして、関数をreturnする。n=1のcountdownが関数returnに達する。n=2のcountdownが関数returnに達する。n=3のcountdownが関数returnに達する。そしてmain関数__main__に戻る。従って全表示は以下のようになる:321Blastoff!自分自身を呼ぶ関数は再帰的で、このプロセスを再帰処理(recursion)という。もう一つの例は文字列を表示するプログラムである:defprint_n(s,n):ifn<=0:returnprintsprint_n(s,n-1)もしn<=0であるとすると、関数returnが実行される。実行は呼び出し関数に戻ることである。この関数の残りの部分はcountdownに類似している。nが0より大きいと文字sをprintし、残りの(n-1)回のprintのために自分自身を呼んでいる。従って、文字sのprintは1+(n-1),つまりn回のprintがなされる。このような簡単な例ではforループの方が易しいかもしれないが、後にforループではプログラムを書くことが難しく、再帰では容易な例に出会うことになる。そのために少し早めに紹介した。5.9再帰関数のスタック図3.10節で関数の呼び出しに際してプログラムの状態を示すためにスタック図を用いた。同じ図が再帰関数の振る舞いを調べるのに便利だ。関数は呼ばれる度にPythonは関数フレームを生成する。このフレームには関数内のローカル変数や仮引数が含まれる。再帰関数の場合には、同時にスタック上にある関数は一つ以上であるかもしれない。\x0c'}, {'id': '7d4175fe-bc08-4f04-bbb9-0fb8d06911b5', 'page': 73, 'text': '5.10.\n無制限な再帰\n73\n\ncountdown\ncountdown\ncountdown\ncountdown\nn\n3\nn\n2\nn\n1\nn\n0\n図5.1: スタック図\n図5.1 はn=3 で呼ばれたcountdown 関数のスタック図である。いつものようにス\nタックの最上段は__main__のフレームである。このフレームは__main__で生成さ\nれた変数や引数が不明なので、空白である。四つcountdown フレームは仮引数の\nn の値が異なっている。スタックの底はn=0 のフレームで基底ケース\n(base case)\nと呼ばれている。これは再帰的な呼び出しをしないのでこれ以上のフレームは存\n在しない。\n練習問題5.1 s = ’Hello’、n=2 で呼ばれたprint n 関数のスタック図を描け。\n練習問題5.2 仮引数として関数オブジェクトと一つの数n を仮引数とする再帰関\n数do n を作成せよ。この関数は問題の関数をn 回実行する。\n5.10\n無制限な再帰\nもしある再帰処理が基底ケースを持たないとすると、再帰的な呼び出しは永遠\nに続くことになる。これは無制限な再帰(infinite recursion)として知られてい\nて、一般に健康なアイデアではない。以下はその最小なプログラムの例である:\ndef recures():\nrecures()\n大抵の開発環境では、このような無制限な再帰を含むプログラムは永遠に実行さ\nれることはない。Python は再帰の深さの限界値に達するとエラーメッセージを吐\nき出す:\nFile "", line 2, in recures\nrecures()\n'}, {'id': '5a623d06-573c-4410-847d-e8d1d0283bd1', 'page': 74, 'text': '74\n第5 章\n条件文と再帰\nFile "", line 2, in recures\nrecures()\nFile "", line 2, in recures\nrecures()\n\u3000.....\nFile "", line 2, in recures\nrecures()\nRuntimeError: maximum recursion depth exceeded\nこのトレースバックは前章でみたものより長めである。このエラー発生時には、ス\nタックには1000 個のrecures フレームができる。\n5.11\nキーボード入力\nこれまで生成したプログラムはユーザからの入力を受け付ける機能がなく、い\nつも同じ動きをするもであった。\nPython2 ではキーボードから入力を受け付けるraw input という組み込み関数\nを提供している。Python3 ではinput だ。この関数が呼ばれると、実行は停止し、\nユーザからの入力待ちになる。ユーザはReturn キーやEnter キーを入力すると、\nプログラムは再実行され、raw input はユーザが入力したものを文字列として戻\nす。つまり、\n>>> input = raw_input()\nWhat are you waiting for?\n>>> print input\nWhat are you waiting for?\nユーザからの入力を得る前に、ユーザに何を入力すべきか知らせることが賢明で\nある。関数raw input は入力促進文字列を引数として受け取ることができる。つ\nまり、\n>>> name = raw_input(’what...is your name\\n’)\nwhat...is your name?\nArthur, King of the Britons!\n>>> print name\nArthur, King of the Britons!\n'}, {'id': 'f7907342-20cb-42b5-9207-863418b5bdf5', 'page': 75, 'text': '5.12.デバッギング75入力促進文字列の最後にあるシークェンス\\nは改行(newline)の記号で、これで改行が起きる。だから、入力文字はこの入力促進文字列の下に現れる訳である。ユーザが整数値を入力すると期待できるときにはその戻り値を整数型に変換するとよい:>>>prompt=’what...istheairspeedvelocityofanunladenswallow?\\n’>>>speed=raw_input(prompt)what...istheairspeedvelocityofanunladenswallow?17>>>int(speed)17しかし、ユーザが数字以外のものを入力しようとするとエラーが出る:>>>speed=raw_input(prompt)what...istheairspeedvelocityofanunladenswallow?whatdoyoumean.anAfricanoraEuropeanswallow?>>>int(speed)ValueError:invalidliteralforint()この種のエラーを処理する方法は後に学ぶ。5.12デバッギング\u3000エラーが発生したとき表示されるPythonのトレースバックは多くの情報を含んでいる。しかし、スタック上のフレームが多数あるときには、その量の多さに圧倒される。その中で有用な情報は•どのようなエラーか•どこで起きたか構文エラーの場所を特定するのは一般的に易しいが、意外なものもある。空白やタブは見えないし無視しがちなので、空白によるエラーは陥りやすい:>>>x=5>>>y=6File"",line1y=6^IndentationError:unexpectedindent\x0c'}, {'id': '33a37a41-4905-4cda-bbc4-22249e8d5091', 'page': 76, 'text': '76第5章条件文と再帰この例では第二行が一つの空白でインデントされているのが問題である。しかし、エラーメッセージはyを指していて、誤解を生む可能性がある。一般にエラーメッセージは問題が発生を確認した場所を示すが、実際のエラーはそれ以前に原因があることもあり得る。ある場合では前の行にそれがある。実行時エラーでも同様だ。あなたは信号・雑音比をデシベルで計算しようしているとしよう。式はSNRdb=10log10(Psignal/Pnoise)と書ける。これをPythonで書こうとすると以下のようになる:importmathsignal_power=9noise_power=10ratio=signal_power/noise_powerdecibels=10*math.log10(ratio)printdecibels実行してみるとエラーメッセージに出会う:Traceback(mostrecentcalllast):File"decibels.py",line5,indecibels=10*math.log10(ratio)ValueError:mathdomainerrorこのエラーメッセージは5行目を指しているが、ここにはおかしなところがない。変数ratioをprintしてみることは助けになるかもしれない。結果は0である。つまり、問題は4行目にある。二つのint型のわり算は小数点以下切り捨てになる。問題の解決は浮動小数点で計算すればよい。一般にエラーメッセージは問題をどこで発見したかをわれわれに告げる。しかし、多くの場合そこはエラーの原因になる場所ではない。Python3ではこの例はエラーにならない。除算演算子は被演算子がint型であっても浮動小数点演算をするからである。5.13語句モジュラ演算子(modulusoperator):一つの整数をもう一つの整数で割ったときの余りを生成する演算子でパーセント記号、(%)が用いられる。.ブール代数表現(booleanexpression):その値が「真」(True)か「偽」(False)になるような表式。.\x0c'}, {'id': '0d5c711f-7598-40f6-bd75-a059cfcbd129', 'page': 77, 'text': '5.14.練習問題77比較演算子(relationaloperators):二つの被演算子を比較し、「真」、「偽」の値を返す演算子。==、!=、>、<、>=,そして<=がある。.論理演算子(logicaloperators):ブール代数表現を結合させるための演算子。and,、orとしてnotがある。.条件文(conditionalstatement):ある条件に従って実行の流れを制御するための文。条件(condition):どちらの分岐を実行するか決めるための条件文の中で使われるブール代数表現。複合文(compoundstatements):一つのヘッダーと一つのボディから構成される文。ヘッダーはコロン(:)で終わり、ボディはヘッダーの対してインデントされる。分岐処理(branches):条件文の中で二者択一的に実行される文の一方。条件文の連鎖(chainedconditional):二者択一の条件文をelif文を使って連鎖させる。入れ子の条件処理(nestedconditionals):条件文の分岐処理の中で条件文を使う。再帰処理(recursion):実行中の関数のなかでその関数を呼ぶ処理。基底ケース(basecase):再帰関数の中でその関数を再帰的に呼ばない分岐処理。無制限な再帰(infiniterecursion):基底ケースを持たないまたはこれに達することがない再帰処理。最終的には無制限な再帰は実行エラーとなる。5.14練習問題練習問題5.3フェルマーの最後の定理は以下のような代数式を満たす整数a,b,cは存在しないと言う:an+bn=cn\u3000ここでnは2より大きな任意の整数である。\x0c'}, {'id': '7f51096f-f7c8-44f9-b679-ef2131dd0281', 'page': 78, 'text': '78\n第5 章\n条件文と再帰\n1. 四つ仮引数a,b,c,n を持つ関数check fermat を作成せよ。この関数はn が\n2 より大きくしかも\nan + bn = cn\nが成立したら、\n「おやまあ、フェルマーは間違っている」と表示し、さもな\nいと「だめだ。成り立たない」と表示する。\n2. ユーザにたいして整数a,b,c,n の入力請求をして、これらを整数に変換し、\n関数check fermat でこれらの整数値がフェルマーの定理を満たさないかど\nうかを示す関数を作成せよ。\n練習問題5.4 三つの楊枝を使って三角形を作ることを試みる。例えば、一つが12\nインチの楊枝で、他の二つは1 インチの楊枝だとしよう。これでは明らかに端と\n端を接して三角形を作ることはできない。三つの任意の値に対して、三角形をつ\nくる条件を調べることができる。\n三角形の一つの辺の長さが他の二辺の長さの和より大きいとこれでは三角形は\n作れない。これ以外であれば作れる(二つの辺の長さは他の一辺に等しいときは、\n「縮退」三角形という)\n1. 三つ整数を引数とする関数is triangle を作成せよ。この関数ではこれらの\n長さの楊枝から三角形が作れるときは"yes"をprint し、それ以外は"no"を\nprint する。\n2. ユーザに三つの楊枝の長さを入力させ、関数is triangle で検定するプログ\nラムを作成せよ。\n以下の練習問題は第四章のTurtleWorld を使う。\n練習問題5.5 以下の関数を読みそれがどのようなことをしているか明らかにせよ。\nそしてこれを実行してみよう(第四章の例を参照のこと)\n。\ndef draw(t, lenght, n):\nif n== 0:\nreturn\nangle = 50\nfd(t, lenght*n)\nlt(t, angle)\ndraw(t, lenght, n-1)\nrt(t, 2*angle)\ndraw(t, lenght, n-1)\nlt(t, angle)\nbk(t, lenght*n)\n'}, {'id': '06dea5e5-7f6e-4152-92c0-c3f9a6eefee7', 'page': 79, 'text': '5.14.練習問題79図5.2:コッホ曲線.練習問題5.6コッホ曲線は図5.2のようなフラクタル図形である。長さxの線分からコッホ曲線を描くには以下のような手順が必要になる:1.x/3の長さでコッホ曲線を描く2.60度左転回3.x/3の長さでコッホ曲線を描く4.120度右転回5.x/3の長さでコッホ曲線を描く6.60度左転回7.x/3の長さでコッホ曲線を描く例外はxが3よい小さいときで、長さxの直線を描くことでよい。1.turtleと長さを仮引数とする関数kochを作成せよ。この関数はこの与えられた長さでコッホ曲線を描く。2.関数snowflakeを三つのコッホ曲線から作成せよ。この関数は雪の微片の外周を表現する。解答例:http://thinkpython.com/code/koch.py3.コッホ曲線はいろんな具合に一般化できる。http://en.wikipedia.org/wiki/koch_snowflake参考にして自分の好みの\u3000図形を描くプログラムを作ってみよう。\x0c'}, {'id': 'efc7a0a8-72f9-4cfb-b2d7-fa47db74d2a7', 'page': 1, 'text': ''}, {'id': 'bc7509d0-284b-4f74-babe-0cb4f8903e46', 'page': 81, 'text': '81第6章結果を生む関数6.1戻り値組み込み関数の多く、特に数学関数は結果を生成する。このような関数を呼ぶと結果を返すので変数に代入するか、表式の一部として使う:e=math.exp(1.0)height=radius*math.sin(radians)これまでに作成した関数は戻り値がないvoid型であった。それらはなにかをprintし、turtleが動きまわる動作をしたが、戻り値が何もなかった。この章では結果を生む関数を書いてみることにする。第一の例は関数areaである。この関数は半径の値を引数として円の面積の値を戻す関数である:defarea(radius):temp=math.pi*radius**2returntempreturn文については既に触れたが、結果を生む関数では、return文には表式を付随する。この文は、「この関数から直ちに戻り、以下の表式を返り値として使え」という意味だ。この表式は任意で、以下のように書くこともできる:defarea(radius):returnmath.pi*radius**2しかし、tempのような一時変数(temporaryvariables)の導入はデバッグを容易にしてくれることがある。ときとして、多重のreturn文の使用が有益なことがある。とくに条件文による分枝がある場合には、以下のようになる:defabsolute_value(x):ifx<0:return-xelse:returnx\x0c'}, {'id': 'a9f7a245-b016-4b43-8db9-9b0a0a688c7e', 'page': 82, 'text': '82\n第6 章\n結果を生む関数\nこれらのreturn 文は二者択一なので、return 文はどちらかが実行される。\n\u3000関数でreturn 文が実行されると、関数は他の文を実行せず、直ちに関数の動\n作は終了する。このようにreturn 文より後に置いた文や決して実行の流れが到達\nしないところにあるコードは死コード(dead code)と言われる。\n結果を生む関数ではプログラムで想定される全ての可能な経路が必ずreturn 文\nに到達することを確認する必要がある:\ndef abusolute_value(x):\nif x < 0:\nreturn x\nif x > 0:\nreturn x\nこの関数は正しくない。x の値がたまたま0 であると、この関数はreturn 文に到\n達できない。関数の実行は関数の終わりにあるreturn 文で戻ることになる。この\n戻り値はノン(None)であり、0 ではない。つまり\n>>>print absolute_value(0)\nNone\nPython は組み込み関数として絶対値を計算するabs 関数を提供している。\n練習問題6.1 比較関数、つまりもしもx > y ならば戻り値は1、x == y ならば、\n0、x < y ならば、-1 を返す関数を作成せよ。\n6.2\n段階的な改良法\n大きな関数を書き始めると、多分にデバッグに多くの時間を使うことになる。こ\nのような複雑なプログラムに取りかかると段階的な改良法(incremental devel-\nopment)と呼ばれている方法を取りたくなるだろう。この方法の目標は一時に追\n加・検証するコードを小刻みにし、膨大なデバッギングを避けることにある。\n例をあげてみよう。与えられた二点とからこの二点間の距離を求める問題だ。\nピタゴラスの定理から\ndistance =\n\x02\n(x2 −x1)2 + (y2 −y1)2\n第一ステップはこの関数distance はPython ではどんな形になるかなと考察する\nことである。換言すれば、入力(仮引数)は何で、出力(戻り値)は何か?\n'}, {'id': '38777fb7-bee5-476b-a13d-8082441b4e83', 'page': 83, 'text': '6.2.段階的な改良法83この例では入力は二つ点、つまり四つの数値であり、戻り値は計算された距離になるであろうから浮動小数点数の値である。ここまで分かると関数の骨格が書ける:defdistance(x1,y1,x2,y2):return0.0この版では戻り値は常に0なので距離の計算はしていない。しかし、この関数は構文的には正しく、ちゃんと動く。もつと複雑に関数を仕上げる前に関数の検証としてこれは使える。適当な引数を入れて検証する:>>>distance(1,2,4,6)0.0水平方向の距離が3、垂直方向の距離が4の二点をわざわざ選んだ。これには理由があり二点間の距離は5となり、事前に答えが分かり関数の動作検証に都合がよいからだ。この時点で関数distanceは構文的には問題ないことが確認できた。次ぎにくる作業は関数のボディを書くことである。これにはこの二点のx方向の差、x2-x1、y方向の差、y2-y1を表示してみることだろう:defdistance(x1,y1,x2,y2):dx=x2-x1dy=y2-y1print’dxis’,dxprint’dyis’,dyreturn0.0この関数を実行してみると、“dxis3”、“dyis4”と表示されるはずだ。次ぎのステップはdxとdyの二乗の和を求めることになる:defdistance(x1,y1,x2,y2):dx=x2-x1dy=y2-y1dsquared=dx**2+dy**2print’dsquaredis:’,dquaredreturn0.0ここでまたこれを実行して出力を調べる。最後は平方根を計算する関数math.sqrtを使いその結果をreturn文で返す:\x0c'}, {'id': '3f70c8e5-a119-48b5-88e5-86d59ff1c0ca', 'page': 84, 'text': '84第6章結果を生む関数defdistance(x1,y1,x2,y2):dx=x2?x1dy=y2?y1dsquared=dx**2+dy**2result=math.sqrt(dsquared)returnresultこれがうまく動くことが確認できれば完了である。そうでなければ、変数resultをreturn文の前で表示してみるとよい。この最終版の関数自体は何も表示しない。print文の挿入はデバッギングに有効だ。デバッグが終わったらこのprint文は削除する。このようなコードは足場組み(scaffolding)と呼ばれる。というのは、これらのprint文はプログラムを完成させることに有効だが、最終に生成されたものには含ませないからだ。プログラミングを始めた最初のころは一度に追加するのは1〜2行にしておくとよい。慣れてきたら、それより大きなまとまりを一度に書き、デバッグすることができるようになるはずだ。いずれにせよ、この段階的な改良法は時間の節約になる。このプロセスの特徴を箇条書きにすると以下のようになる:1.実行可能な小さなプログラムから始めて、少しずつ段階的に変更を加える。いつの段階でもエラーが発生したら原因を突き止め修正しておく。2.一時変数を使い途中の値を保存し、それを必要に応じて表示、検証できるようにする。3.最終的なプログラムがうまく動くようになったら、足場の撤去やプログラムが読み難くならない範囲で、複数の文を一つの複合表式に置き換えなどの作業をする。練習問題6.2直角三角形の二辺の長さを引数として受け取り、斜辺の長さを戻り値として返す関数hypotenuseを段階的な改良法を使って書け。各段階の状況を記録して置くこと。6.3合成関数さて、これまでの議論で関数の中で別な関数を呼ぶことも可能だということを予想できたと思う。このような機能を複合関数(composition)という。このような例として、円の中心の座標と円周上の座標の二点の座標を引数としてその円の面積を計算する関数を書いてみよう。\x0c'}, {'id': 'adaca04b-be81-42f4-932c-bfb2b74a5141', 'page': 85, 'text': '6.4.\nブール代数関数\n85\n変数xc、yc に円の中心の座標の値が保存され、xp、xp に円周上の座標の値が保\n存されているとしよう。第一にすべきはこの二点を使って円の半径(radius)を\n出すことである。これには関数distance を使う:\nradius = distance(xc, yc, xp, yp)\n次はこの半径から面積を出す関数area を使う。ここでは形式的にのみ書いて置く:\nresult = area(radius)\nこれらを一纏めにして一つの関数とする:\ndef circle_area(xc, yc, xp, yp):\nradius = distance(xc, yc, xp, yp)\nresult = area(radius)\nreturn result\nradius やresult の一時変数はプログラムの開発途上やデバッグには有効である\nが、最終的なプログラムでは関数を入れ子にして簡潔に書くこともできる:\ndef circle_area(xc, yc, xp, yp):\nreturn area(distance(xc, yc, xp, yp))\n6.4\nブール代数関数\nブーリアン(真理値)を返す関数は関数内部で複雑な検証を隠す上で有効なも\nのである。例を上げる:\ndef is_divisible(x, y)\nif x % y == 0:\nreturn True\nelse:\nreturn False\nブール代数関数には二者択一のような関数名がよく付けられる。\n関数is divisible\nはx がy で割り切れるかどうかの結果をTrue やFalse として返す関数である。\n実行例:\n>>> is_divisible(6, 4)\nFalse\n>>> is_divisible(6, 3)\nTrue\n'}, {'id': 'f715ab33-a89f-4ec6-a65a-2ddb2c4a5e22', 'page': 86, 'text': '86\n第6 章\n結果を生む関数\n比較演算子の結果の値がブーリアンなので関数は以下のように書ける:\ndef is_divisible(x, y):\nreturn x % y == 0\nブール代数関数はよく条件文の中で使われる:\nif is_divisible(x, y):\nprint ’x is divisible by y’\nもしかしたら以下のように書くかもしれない:\nif is_divisible(x, y)==0:\nprint ’x is divisible by y’\nしかし、この比較は不必要である。\n練習問題6.3 以下の関数is between(x, y, z) を書け。ここでx ≤y ≤z なら\nTrue を返し、これ以外ならばFalse を返す。\n6.5\n再帰関数の拡張\nこれまでの学習はPython の一部分であるが、これまでの学習で得たもので完全\nなプログラミング言語になっていることを確認することは興味深いことであろう。\nこの完全という意味は計算したいと思われる全てのことがこの言語で書けるとい\nうことである。あなたがこれまで作成した如何なるプログラムもこれまで学習し\nたPython を使って書き換えることができる(多分、キーボード、マウス、ディス\nクといった装置を制御するコマンドは別途必要かもしれない)\n。\nこの主張の証明はアラン・チューリングによって初めて自明なことではない課\n題であることが証明された。かれは当時のコンピュータ科学者がそうであったよ\nうに数学者であったが、コンピュータ科学者の嚆矢の一人である。従って、この主\n張はチューリング・テストと言われている。このチューリング定理のもっと完全\nで(従って厳密な)議論は“Introduction to the Theory of Computation”(Michael\nSipser 著) を参照してほしい。\nここでは、これまで学習したツールを使って何ができるかの認識を得るために、\n再帰関数のいくつかを検討することにする。再帰的定義は循環的定義に似ている。\nそれらの定義にはそこでは定義するべきものの参照を含んでいる。\n循環的定義はあまり有用ではない。たとえばこうだ:\n'}, {'id': 'e924616a-a4f8-45b2-9760-d4d5a0cc4ebc', 'page': 87, 'text': '6.5.再帰関数の拡張87vorpal:何かvorpalであるものを記述する形容詞である。こんな定義を辞書でみつけたらまごついてしまうかもしれない。しかし、階乗関数が記号!で記述されているのを探し出したときには、その記述は以下のようなものだろう:0!=1n!=n(n-1)!この定義は0の階乗は1、その他の任意の値、nの階乗はn-1の階乗にnを乗じたものであることを意味する。だから3!は3掛け2!で、2!は2掛け1!で、1!は1掛け0!である。これを纏めると3!は3×2×1×1で6となる。何かを再帰的な定義で記述できたならば、それをPythonでプログラムして評価することができる。第一にすべきは、関数名とその仮引数を決めることである。今回の場合、その関数factorialの仮引数は一つの整数である。つまりdeffactorial(n):となる。この引数がたまたま0であると、この関数の返すべき値は1であるのでdeffactorial(n):ifn==0:return1となる。さもないと、これが興味あるケースであるが、再帰呼び出しでn-1の階乗を求め、それにnを乗ずる計算をすることになる。従って、プログラムは以下の様になる:deffactorial(n):ifn==0:return1else:recurse=factorial(n-1)result=n*recursereturnresultこのプログラムの実行の流れは5.8節で登場したcountdownと似たものになる。nの値を3にしてこの関数を呼んだときの流れを以下に示す:\x0c'}, {'id': '5a574f98-4fb7-4db5-aee8-7a7f0304a40e', 'page': 88, 'text': '88第6章結果を生む関数3は0でないので、第二の分枝をとり、n-1の階乗を計算する。\u3000\u30002は0でないので、第二の分枝をとり、n-1の階乗を計算する。\u3000\u3000\u3000\u30001は0でないので、第二の分枝をとり、n-1の階乗を計算する。\u3000\u3000\u3000\u3000\u3000\u30000は0であるので、第一の分枝をとり、単に1を戻り値として返す。\u3000\u3000\u3000\u3000戻り値1をrecurseに代入、これとn(即ち1)とを乗じた値1を返す。\u3000\u3000戻り値1をrecurseに代入、これとn(即ち2)とを乗じた値2を返す。戻り値2をrecurseに代入、これとn(即ち3)とを乗じた値6を返す。これでこの関数を最初に呼んだ関数に戻る。図6.1にこの関数呼び出しのスタック図を示した。戻り値はスタック図を繋ぐ経路に添付した。各フレームでは戻り値はそのフレームのresultが持っている値である。最後のフレームでは流れはif文の第一分枝になるので、ローカル変数recuresとresultは存在しない。n3recurse2recurse1recurse1factorialn2n1n0factorialfactorialfactorial11261result26resultresult図6.1:スタック図.6.6信用して跳び越える実行の流れを追うことはプログラムを読むひとつの方法であるが、直ぐに複雑になる。別な方法は私が「信用して跳び越える」と呼ぶ方法である。関数に出会ったらその関数内の流れを追跡するかわりに、その関数は正常に動作し、正しい戻り値を返すとみなし、この関数内の実行の流れをスキップする。このようなスキップは既に組み込み関数では経験している。math.expやmath.cosの関数ではこれらの関数内の流れには頓着しない。われわれはこれらの関数は優秀なプログラマーが作成したもので、正常に動作するとみなしている。同じことが自作関数でもいえる。例えば、6.4節で作成したdivisible関数だ。コードの吟味や検証で正常に動作することが確証できたら、以後はこのボディの実行の流れには頓着しない。\x0c'}, {'id': '2cd93109-0e81-43be-a3b2-c64d55654e96', 'page': 89, 'text': '6.7.もう1つの例題89同じことが再帰関数でもいえる。再帰関数の呼び出しに出会ったら、その関数内の流れを追わず、その再帰的な呼び出しは正常に動作する(正しい戻り値を返す)とみなす。そして、「もしn-1の階乗が計算できたら、それでnの階乗が計算できるか?」と自問してみる。この場合、n-1の階乗にnを乗ずれば答えになることは明らかである。勿論、まだ書き上げてもいない関数が正常に動作するとみなすことは少々奇妙であるが、これは「信用して跳び越える」という言葉の所以である。6.7もう1つの例題関数factorialに引き続き数学的な再帰関数として引き合いに出されるfibonacci関数である。この関数の定義は以下のようである:fibonacci(0)=0fibonacci(1)=1fibonacci(n)=fibonacci(n-1)+fibonacci(n-2)(http://en.wikipedia.org/wiki/Fibonacci_numberも参照のこと)これをPythonに翻訳するとこんな風になる:deffibonacci(n):ifn==0:return0elifn==1:return1else:returnfibonacci(n-1)+fibonacci(n-2)この関数の実行の流れをまともに追跡すると、nが小さい場合でさえ頭が爆発してしまう。しかし、「信用して跳び越える」方法に従って、二つ再帰呼び出しは正常に実行されると仮定すると、第三分枝の計算で正しい答えが戻ることは自明となる。6.8型の検証関数factorialを引数の値に1.5を入れて呼ぶとどうなるだろうか?\x0c'}, {'id': '68c3eb95-ff7d-430c-9793-34c766e4321b', 'page': 90, 'text': '90第6章結果を生む関数>>>factorial(1.5)RuntimeError:maximumrecursiondepthexceededこの実行時のエラーは制限なしの再帰に起因するようにみえる。どうしてそんなことが起きるのか?この関数には基底ケースが考慮されているではないか。引数nが整数でないと、この基底ケースをすり抜けてしまい、制限なしの再帰になるのだ。上の引数では、最初の再帰で引数nの値は0.5になり、二回目は-0.5となる。だから基底ケースであるn=0は実現しない。対策としては二つの方向がある。一つはfactorialを拡張して引数は浮動小数点数にも対応できるようにする。二つ目は引数型を検証するように書き改めることである。第一はガンマ関数への拡張であり、この本の範囲を超えるので、第二番目の方法を紹介する。引数の型を調べるために組み込み関数isinstanceを使う。これと同時に引数が正の数であることも調べることにする。プログラムは以下のようになる:deffactorial(n):ifnotisinstance(n,int):print’Factorialisonlydefinedforintegers.’returnNoneelifn<0:print’Factorialisnotdefinedfornegativeintegers.’returnNoneelifn==0:return1else:returnn*factorial(n-1)第一番目の基底ケースは非整数対策であり、第二番目の基底ケースは負整数対策である。どちらも何らかの問題があるというメッセージを吐き出し、戻り値はNoneである:>>>factorial(’fred’)Factorialisonlydefinedforintegers.None>>>factorial(-2)Factorialisnotdefinedfornegativeintegers.None\x0c'}, {'id': 'e0c29637-0428-43c6-ba5e-f56a756f1076', 'page': 91, 'text': '6.9.デバッギング91この二つの検証を通過したら、引数nは正か0の整数である。この状態になれば、この再帰呼び出しは有限回で停止することが証明できる。このプログラムはときとして保護回路(guardian)と呼ばれる手法を例示したことになる。最初の二分枝は保護回路で実行時エラーを引き起こすだろうと思われる値からコードを保護する役目をしている。11.3節ではエラーの表示の替わりに例外処理を実行するもっと融通の効く手法に出会うはずだ。6.9デバッギング\u3000大きなプログラムをそれより小さい関数に分割することはそれ自体でデバッグの極自然な確認の単位になる。もし関数が正常に動作しないとなると、考えなければならないことは以下の三点である:•関数の事前条件に違反するような引数の問題。•関数の事後条件に違反する問題。•関数の戻り値やそれの使われ方の問題。最初の可能性を除外するには関数の先頭にprint文を挿入し、仮引数の値(そして型も)を表示してみるとよい。または、関数のコード自体にこの機能を持たせるとよい。仮引数に問題がないとすると、次は関数のreturn文の前で戻り値を表示してみる。もし可能ならば、この戻り値を紙で計算してみる。簡単な引数の値であれば、その関数の戻り値を手計算することは容易い(6.2節をみよ)。関数の最初と最後にprint文を追加・挿入することは実行の流れを可視化する上で有用である。以下はfactorialにprint文を追加したものである:deffactorial(n):space=’’*(4*n)printspace,’factorial’,nifn==0:printspace,’returning1’return1else:recurse=factorial(n-1)result=n*recurseprintspace,’returning’,resultreturnresult\x0c'}, {'id': 'fc99db10-9918-4f8e-8854-501e710677ea', 'page': 92, 'text': '92第6章結果を生む関数変数spaceは出力のインデントを制御する空白文字列である。factorial(5)の結果を示す:factorial5factorial4factorial3factorial2factorial1factorial0returning1returning1returning2returning6returning24returning120実行の流れについて混乱してしまったときは、このような出力は有用になる。このような効果的な足場を据えることには時間が掛かるが、ちょっとした足場もデバッギングの時間を節約することができる。6.10新しい語句一時変数(temporaryvariables):複雑な計算の際に中間結果を保存する目的で使われる変数。死コード(deadcode):如何なるばあいでも到達しないプログラムの部分。return文の後にあることが多い。ノン(None):returnの無い関数や戻り値がない関数が返す特別な値。段階的な改良法(incrementaldevelopment):一時に少量の追加とテストを行いつつプログラムを開発する手法。足場組み(scaffolding):最終版では削除されるがプログラム開発の途上で使われるコード。保護回路(guardian):エラーを引き起こす状況を検出及び回避するために条件文を使うプログラムの部分。\x0c'}, {'id': 'c44e28f2-d3b6-46b4-82b1-0b35f09a48d5', 'page': 93, 'text': '6.11.練習問題936.11練習問題練習問題6.4以下のようなプログラムのスタック図を示せ。さらに実行したとき、如何なる表示が出るか答えよ。defb(z):prod=a(z,z)printz,prodreturnproddefa(x,y):x=x+1returnx*ydefc(x,y,z):total=x+y+zsquare=b(total)**2returnsquarex=1y=x+1printc(x,y+3,x+y)解答例:http://thinkpython.com/stack_diagram.py練習問題6.5Ackermann関数は以下のように定義される:A(m,n)=⎧⎪⎪⎨⎪⎪⎩n+1ifm=0A(m−1,1)ifm>0andn=0A(m−1,A(m,n−1))ifm>0andn>0.⎫⎪⎪⎬⎪⎪⎭(この関数については、http://en.wikipedia.org/wiki/ackermann_functionを参照のこと)Ackermann関数の値を計算する関数ackを作成せよ。そして、ack(3,4)の値125を調べよ。大きなmやnでは何が起こるか調べよ。解答例:http://thinkpython.com/code/ackermann.py練習問題6.6回文とは、例えば“noon”や“redivider”のように後から読んでも、前から読んでも同じ綴りを持つ言葉である。再帰的には、もしある言葉の最初と最後\x0c'}, {'id': '89eb5a0f-aaa1-498a-b968-a58d5539412d', 'page': 94, 'text': '94\n第6 章\n結果を生む関数\nの文字が同じで、残った中間の文字列が回文であるなら、この言葉は回文である。\n次ぎのような文字列処理の関数はその文字列の最初の文字、最後の文字、中間\nの文字列を戻り値とする:\ndef first(word):\nreturn word[0]\ndef last(word):\nreturn word[-1]\ndef middle(word):\nreturn word[1:-1]\nこれらの関数の詳細は第八章で議論することになる。\n• これらの関数をpalindrome.py と名付けたファイルとして生成せよ。関数\nmiddle に二文字の言葉を入れると戻り値はどうなるか?一文字ではどうか?\n一文字も含まない文字列、つまり” ではどうか?\n• 引数として文字列を受け取る関数is\npalindrome.py を作れ。この関数の\n戻り値はこの文字列が回文であるとTrue、さもないとFalse である。文字\n列の長さは関数len で調べられることに注意。\n解答例:http://thinkpython.com/code/palindrome_soln.py\n練習問題6.7 一つの数a は、もしもこの数がb で割り切れ、しかもa/b がbのべ\nき乗であるとき、b のべき乗である。\n二つの仮引数a、b を持つ関数is power を作成せよ。この関数はa がb のべき乗\nであるときにTrue を返す。\n注)基底ケースについて考慮せよ。\n練習問題6.8 二つ整数a、bの最大公約数(GCD)はこの二つの整数を除算して\n余りがない整数のなかで最も大きな整数である。\n最大公約数を探す一つの方法はユーグリッド互除法である。この方法はa をb\nで割ったときの余りをr とすると、gcd(a, b) = gcd(b, r) であることからきている。\n基底ケースはgcd(a, 0) = a を使う。\n二つの整数a、b を仮引数とする関数gcd を作成せよ。戻り値はこの二つの数の\n最大公約数である。\n(参考:http://en.wikipedia.org/wiki/Euclidean_algorithm)\n'}, {'id': '8888f35c-ce8f-4f1f-9d4a-3df92a9ed42e', 'page': 95, 'text': '6.11.練習問題95この練習問題の出典は、AbelsonandSussman,“StructureandInterpretationofComputerPrograms”である。\x0c'}, {'id': 'b73303c2-7255-4825-80e2-f7767ea2a33c', 'page': 1, 'text': ''}, {'id': 'aa60d060-6e21-446e-9768-dc35b04082ea', 'page': 97, 'text': '97第7章繰り返し処理7.1多重代入これまで経験したことから分かるが、同じ変数に複数回の代入をすることは合法である。この新しい代入で変数はこの新しい値を参照することになる(古い値の参照は停止する)。bruce=5printbruce,bruce=7printbruceこのプログラムの表示は57となる。第一回目のbruceの表示で5を、次には7を表示する。図7.1には多重代入(multipleassignment)のスタック図の様子を示した。75bruce図7.1:状態図.多重代入で重要なことは代入処理と等値の違いを明確にしておくことだ。Pythonでは等号(=)は代入に使われるので、a=bのような文を等値と解釈しがちである。これは間違いだ。第一に、等値は左辺と右辺が対称だが、代入は対称でない。例えば、数学的では、a=7と7=aは同じ意味だが、Pythonでは違う。更に、数学では等値は常に真が偽の値を持つ。だから、もしa=bならaとbとは常に等値である。Pythonの代入文でも二つの変数の値を同じにすることができるが、常に同じ値を持つとは限らない。a=5b=a#aとbとは同じ値を持つa=3#aとbとは最早同じ値ではない\x0c'}, {'id': 'adeef785-54d5-478b-ae68-04eb8a968969', 'page': 98, 'text': '98\n第7 章\n繰り返し処理\n第三行目でa の値が変わるが、b の値は変わらない。だから、この二つは最早等し\nくはない。\n多重代入は頻繁に使うが、注意が必要だ。変数の値が頻繁に変わるのでプログ\nラムが読み辛いものになりがちでデバッグが難しくなる。\n7.2\n変数更新\n最も頻繁に遭遇する多重代入は変数更新(update)である。新しい値が古い値\nに依存するかたちをとる:\nx = x + 1\nこの意味は、\n「x の現在の値に1 を加えてものをx の新しい値として更新せよ」\n。\n存在しない変数の値の更新は、Python は代入の前に右辺を評価しなければなら\nないので、エラーになる。\n>>> x = x + 1\nNameError: name ’x’ is not defined\n変数の更新をする前に、変数は初期化(initialize)\n、一般に簡単な代入だが、しな\nければならない。\n>>> x = 0\n>>> x = x + 1\n変数の更新で1 を加えて更新することをインクリメント(increment)、1 を減じ\nて更新することをデクリメント(decrement)と特別な名称が付けられている。\n7.3\nwhile文\nコンピュータは繰り返しを自動的に実行するタスクに頻繁に使われる。同じか類\n似の処理を間違いなしで繰り返し実行するタスクはヒトでは苦手だが、コンピュー\nタは得意としている。\nこれまでcountdown とprint n というプログラムをみてきた。これらのプログ\nラムは再帰を使っているが、繰り返し処理(iteration)の例だ。繰り返し処理は\n頻繁に使うので、Python は言語仕様として豊富な繰り返し機能を提供している。\nその一つが4.2 節で紹介したfor 文である。これは後に議論する。\nもう一つがwhile 文である。countdown をwhile で書いたものが以下である:\n'}, {'id': '249acf51-76b3-43c8-a806-a4fc941ffa17', 'page': 99, 'text': '7.3.while文99defcountdown(n):whilen>0:printnn=n?1print’Blastoff!’このwhile文は英文を読むように読める。つまり、「nが0より大きい間、nの値を表示し、nの値を1減じなさい。nが0になったとき単語Blastoff!を表示しなさい」。もう少し厳密にwhile文の実行の流れを述べてみる:•条件を評価し、真か偽を得る。•偽ならば、while文のブロックを抜け、次ぎの文から実行を続ける。•真ならば、whike文のボディを実行して、1に戻る。この種の実行の流れをループ(loop)と称する。ループの本体の中で一つか二つの変数を更新し、ループの終了が保証されているようにしなければならない。さもないとそのループは無限ループ(infinteloop)になる。コンピュータ科学者にとって、「よく泡立て、よく洗い、これを繰り返す」というシャンプーの使用説明はひやかしの尽きない源である。関数countdownのばあいはnの値が有限で、ループを繰り返す度にnが減少するので、いずれは0になることが証明される。一般的にはこれは容易くない:defsequence(n)whilen!=1:printnifn%2==0:#nは偶数n=n/2else:#nは奇数n=n*3+1このループの条件はn!=1、だからループはnが1に達し、条件が偽になるまで続く。毎回nの値が表示され、nが奇数か偶数かが検定される。もし偶数であるとすると、nは2で割られる。奇数であると、nの値はn*3+1に置き換わる。例として3を引数としてこの関数が呼ばれると、表示は3,10,5,16,8,4,2,1となる。\x0c'}, {'id': '4b5a81ee-9926-47a3-af1d-06868f37068c', 'page': 100, 'text': '100\n第7 章\n繰り返し処理\nn はときとして増加、またときとして減少するので、このプログラムでn が1 に\n達し、プログラムが終了することを証明するのは自明ではない。例えば、n が2 の\nべき乗で出発するとn は1に達すまで偶数のままである。前の例は16 以降の系列\nはこのケースである。\n如何なる正整数n に対してもこのループが停止することを証明することは難し\nい問題だ。これまで、これをできた人がいないし、できないということを証明で\nきた人もいない。\n(参考:http://en.wikipedia.org/wiki/Collatz_conjecture.)\n練習問題7.1 関数print n を再帰でなく、繰り返し処理をするプログラムに書き\n換えよ。\n7.4\nブレイク\nときとしてループの本体を実行している途中まで何時ループから抜け出るべき\nかを知ることができない場合がある。このときはbreak 文でループを脱出する。\n例えば、done とタイプされるまでユーザの入力を受け付けることを考えてみよ\nう。プログラムは以下のようになる:\nwhile True:\nline = raw_input(’<’)\nif line == ’done’:\nbreak\nprint line\nprint ’Done!’\nループの条件は常に真であるから、このループはbreak に遭遇するまで続く。そ\nの繰り返し毎にユーザ入力促進が一つのアングルブラケットで示される。ユーザ\nがdone と入力すると、break 文でループを抜ける。そうでなければ、入力したも\nのが何であってもそれを表示してループの先頭に戻る。実行例:\n> not done\nnot done\n> done\nDone!\nこれはwhile 文の使い方としては極普通である。ループの検定はどこでもできるし\n(ループの先頭でなくても)\n、終了条件を否定的(これが起きるまで続く)でなく、\n確定的(これを起きたとき終了)にも表現できる。\n'}, {'id': '50eac21a-bd90-409a-8c9b-c04bfd7d7bd6', 'page': 101, 'text': '7.5.\n平方根\n101\n7.5\n平方根\nループ処理は適当な答えから出発した数値計算でその答えを徐々に改良して行\nくときによく使われる。\n例としてある数の平方根を求める手法としてニュートン法がある。ある数a の\n平方根を知りたいとしよう。それに近い数x から出発して、以下の式を使うとさ\nらによい近似値、y が得られる:\ny = x + a/x\n2\n例として、a が4 で、x が3 のばあいと計算してみる:\n>>> a = 4\n>>> x = 3.0\n>>> y = (x + a/x) / 2\n>>> print y\n2.16666666667\nこの値はより真の値(\n√\n4 = 2 )に近い。この得られた新しい値を使ってプロセス\nを繰り返すともっと近い値が得られる:\n>>> x = y\n>>> y = (x + a/x) / 2\n>>> print y\n2.00641025641\nこれを数回繰り返すと推定値はほぼ正解になる:\n>>> x = y\n>>> y = (x + a/x) / 2\n>>> print y\n2.00001024003\n>>> x = y\n>>> y = (x + a/x) / 2\n>>> print y\n2.00000000003\n一般に真の値を得るには何回の繰り返しが必要なのかは事前には分からない。し\nかし、推定値の表示が変動しなくなるところで、真の値の達したと考えてよい:\n'}, {'id': 'dd55fd3e-93d4-4be1-9b21-91a67024b5fb', 'page': 102, 'text': '102\n第7 章\n繰り返し処理\n>>> x = y\n>>> y = (x + a/x) / 2\n>>> print y\n2.0\n>>> x = y\n>>> y = (x + a/x) / 2\n>>> print y\n2.0\nつまり、y == x で繰り返しを終了する。纏めると、初期推定値x から出発してそ\nれが変化しなくなるまで繰り返すことにする:\nwhile True:\nprint x\ny = (x + a/x) / 2\nif y == x:\nbreak\nx = y\nこの考えで大部分のa についてはよさそうだ、しかし浮動小数点数の等値に関連\nする問題を含んでいる。浮動小数点数は近似的に正しいのみである。例えば、1/3\nのような有理数、\n√\n2 のような無理数は浮動小数点数では正確には表現できない。\nそこで、x とy が正確に一致するという替わりに、この2つの値の差の絶対値を\n計算し、この差の絶対値に制限を置くことにする。ある値の絶対値は組み込み関\n数abs を使う。\nif abs(y-x) < epsilon\nbreak\nここでepsilon は0.0000001 のような数で、二つの値x,y がどの程度近ければよ\nいかを決める。\n練習問題7.2 以上の議論を基に関数square root を作成せよ。この関数は仮引数\nとして正の数a を持ち、この平方根を求める機能を持つ。平方根はこの関数の戻\nり値とする。\n'}, {'id': '83566c98-c7f9-4742-b2eb-9a389484c004', 'page': 103, 'text': '7.6.アルゴリズム1037.6アルゴリズムニュートン法はアルゴリズム(algorithms)の一例である。それは機械的な方法である種の問題を解く方法のことである(いまの場合は、平方根を求める)。アルゴリズムとは何かを定義することはそんなに易しくない。アルゴリズムでないものから議論したら理解が進むかもしれない。一桁のかけ算を学んだ過程で、九九の表を暗記することになるはずだ。この知識で特別な値を持った100個の場合のかけ算の解法が得られたことになる。しかし、このような知識はアルゴリズム的でない。もしあなたが「怠け者」で、このような暗記をなるべく減らしたいとして頭を使ったとしよう。例えば、9桁の九九はnを他の整数とすると、かけ算の答えは十の位はn-1で、一の位は10-nであるとすればよいと発見したとしよう。この発見は任意の整数nに対して成り立つ操作だ。このようなものがアルゴリズムである。同様に、加算での繰り上げ、減算の繰り下げの操作がアルゴリズムである。アルゴリズムの一つの特徴はその操作に知性を必要としないことだ。それらは最初から最後まで簡単な規則に沿って進められる機械的なプロセスである。わたしの意見では、現在の学校制度がこのような知性を必要としないアルゴリズムの実行の仕方を学ぶことに多くの時間を費やしていることは恥ずかしいことだと思う。これに反して、アルゴリズムを設計することは興味深く、知性的な挑戦であり、プログラミングの中枢である。ヒトが何の苦もなく無意識にやっているいくつかのことをアルゴリズムとして定式化することは最も難しい。自然言語はその卑近な例である。ヒトはそれをやっているが、これまでどのようにしてヒトがそれをやっているのか説明できないでいるし、少なくともアルゴリズム的に定式化できないでいる。7.7デバッギング大きなプログラムを書き始めると、デバッギングに掛ける時間が多くなる。より大きなコードはより多くのエラーの機会があり、より多くの場所にバグが潜んでいることを意味する。時間を短縮する一つの方法は、「二分割デバッグ法」とでもいう方法だ。例えば、100行あるコードを一時にチェックしようとすると100行をみる時間が必要になる。そこで、プログラムを半分に分割する。手頃な半分あたりでよい。そこにprint文(またはおなじ効果が得られるものでよい)を追加し、プログラムを実行してみる。このチェックポイントまでで間違いが分かれば、前半に問題があることになるし、間違いがなければ、問題は後半にあることになる。\x0c'}, {'id': '87ffb95d-c900-4571-9434-bb05b38cf053', 'page': 104, 'text': '104\n第7 章\n繰り返し処理\nこのように毎回二分割して行く。この操作を数回(これは100 よりは少ない)す\nれば、理論的にはコードの1,2行に問題を絞り込むことができる。\n実際には、\n「プログラムの半分」を見つけることが難しいことがあるが、行数を\n数えてまん中を見つけることは意味がない。エラーが潜んでいそうな場所や、値\nをチェ\nックするのに都合がよい場所の目星をつける。そして、エラーがその前後で\n起きているらしいところをチェ\nックポイントとして選べばよい。\n7.8\n語句\n多重代入(multiple assignment)\n:プログラムの実行の過程で一つの変数に一\n回以上の代入をすること。\n変数更新(update)\n:変数の古い値に依存するかたちでその変数の値を更新する。\n初期化(initialize)\n:変数更新をするつもりの変数に特定の値\n(初期値)\nを与える。\nインクリメント(increment)\n:変数の値を1 だけ増加させる変数更新。\nデクリメント(decrement)\n:変数の値を1 だけ減ずる変数更新。\n繰り返し処理(iteration)\n:再帰呼び出しやループを使ってプログラムの一部を\n繰り返し実行する。\n無限ループ(infinte loop)\n:終了条件が満たされることのないループ。\n7.9\n練習問題\n練習問題7.3 この章で扱った平方根を求める関数をテストせよ。test square root\nというプログラムを作り、組み込み関数math.sqrt の結果と比較する以下のよう\nなテーブルを作れ。\n1.0\n1.00000000000\n1.00000000000 0.000000e+00\n2.0\n1.41421356237\n1.41421356237 2.220446e-16\n3.0\n1.73205080757\n1.73205080757 0.000000e+00\n4.0\n2.00000000000\n2.00000000000 0.000000e+00\n5.0\n2.23606797750\n2.23606797750 0.000000e+00\n6.0\n2.44948974278\n2.44948974278 8.881784e-16\n7.0\n2.64575131106\n2.64575131106 0.000000e+00\n'}, {'id': '4fe4445c-be8c-4034-915a-836269fd3cd8', 'page': 105, 'text': '7.9.\n練習問題\n105\n8.0\n2.82842712475\n2.82842712475 4.440892e-16\n9.0\n3.00000000000\n3.00000000000 0.000000e+00\n第一列は数値a、第二列は7.5 節で検討した関数で計算した平方根の値、第三列は\nmath.sqrt で計算した値、第四列は二つの計算結果の差の絶対値である。\n練習問題7.4 組み込み関数eval は文字列を引数として受け取り、Python インタ\nプリタでその文字列を評価する関数である。例えば\n>>> eval(’1+2*3’)\n7\n>>> import math\n>>> eval(’math.sqrt(5)’)\n2.2360679774997898\n>>> eval(’type(math.pi)’)\n\n繰り返しユーザに入力請求をして文字列を入力、それをeval で評価して結果を表\n示するプルグラムeval loop を作成せよ。この関数はユーザが’done’ を入力した\nらループを抜け、最後に評価した値を戻り値とする。\n練習問題7.5 数学者Srinivasa Ramanujan は以下の公式が1/π の近似値を生成す\nることを発見した:\n1\nπ = 2\n√\n2\n9801\n∞\n\nk=0\n(4k)!(1103 + 26390k)\n(k!)43964k\nこの公式を使い関数estimate pi を作成せよ。数列の項の和を求めるところに\nwhile ループを使い、項が充分に小さくなったところ(10−15)でそのループを抜\nける。結果をmath.pi と比較せよ。\n解答例:http://thinkpython.com/code/pi.py\n'}, {'id': '3ef5c68a-5255-4f88-b985-c73f8aba935d', 'page': 1, 'text': ''}, {'id': 'b8d93fdc-0bc8-407a-9430-9b23fea8e12b', 'page': 107, 'text': '107第8章文字列8.1文字列は文字の配列文字列は文字の配列(sequence)である。角括弧で文字列の一つの文字にアクセスすることができる。>>>fruit=’banana’>>>letter=fruit[1]第二の命令では文字列fruitから一文字を選び、それをletterに代入している。角括弧の中の表式はインデックス(index)と呼ばれている。このインデックスはどの文字を選びたいかを示すものである。しかし、結果はあなたが期待したものでないかもしれない:>>>printlettera多くの人々にとっては、’banana’の最初の文字はbであってaではない。しかし、コンピュータ科学者にとっては、インデックスは文字列の最初の文字からのオフセット(片寄り)であり、最初の文字のオフセットは0である。>>>letter=fruit[0]>>>printletterbだから、bは0番目の文字であり、aは1番目の文字、nは2番目の文字である。このインデックスの表式は、変数や演算子を含めて任意であるが、そのインデックスの値は整数でなければならない。さもないとエラーになる:>>>letter=fruit[1.5]TypeError:stringindicesmustbeintegers,notfloat\x0c'}, {'id': '32550cfe-f9ba-466b-9cac-1eeedd2f9fec', 'page': 108, 'text': '108第8章文字列8.2len組み込み関数lenは文字列の長さを返す関数である。>>>fruit=’banana’>>>len(fruit)6文字列の最後の文字を得ようとしたとき、以下のようにするかもしれない:>>>length=len(fruit)>>>last=fruit[length]IndexError:stringindexoutofrangeこのエラーは文字列’banana’にはインデックスの値が6の文字がないことを意味している。文字は0番から数えたので最後の文字は5番となる。だから最後の文字はlengthから1減じたものになる:>>>last=fruit[length-1]>>>printlasta別な方法としては負のインデックスを用いるものだ。文字列の最後の文字から逆向きに数える。だから、fruit[-1]が最後の文字を示し、fruit[-2]が後から二番目の文字となる。8.3forループによる横断処理多くの文字列処理では一文字毎の扱いが沢山現れる。文字列の初めから一文字を抽出し、何かの処理を行い、これを文字列の最後まで続けるといったことがよくある。このような処理のパターンを横断処理(traversal)と呼ぶ。これを実現する一つの方法はwhileループを使うものである:index=0whileindex>> a = 'Monty Python'\n>>> print(a[0:5])\nMonty\n```\n\n演算子 `[:]` で文字列の n 番目の文字から n 番目 (n 番目は含まず) までの文字列を返す。この振る舞いは直感的ではないが、関数`len`を使用するとインデックスは文字と文字の間にあると理解できるかもしれない。 \n\n最初のインデックス (コロンの前) を省略すると、スライスは先頭の文字からとなる。また三番目のインデックスを省略すると文字列の最後までとなる。\n\n```python\n>>> fruit = 'banana'\n>>> fruit[:3]\n'ban'\n>>> fruit[3:]\n'ana'\n```\n\n最初のインデックスが二番目より等しいか大きいと空文字列 (`empty string`) を返す。これは二つの引数で表現される:\n\n```python\n>>> fruit = 'banana'\n>>> fruit[3:3]\n''\n```\n\n空文字列は文字を一つも含まないので文字列の長さは 0 であり、他の性質は普通の文字列と同じである。\n\n練習問題 8.3 `fruit` が与えられた文字列として、`fruit[:]` は何を意味するか?"}, {'id': 'e83151c3-4d84-4992-b180-0c5ea70d122d', 'page': 111, 'text': '8.5.文字列は変更不可1118.5文字列は変更不可文字列の一部を変更しようとして以下のような代入を試みたとする:>>>greeting=’HelloWorld!’>>>greeting[0]=’J’TypeError:’str’objectdoesnotsupportitemassignmentここでオブジェクト(object)は文字列、アイテム(item)は文字列の中の代入しようとした文字である。この段階ではオブジェクト(object)とは値と同じものであるとしておく。後にこの定義は再吟味したい。アイテム(item)は配列の一つの要素である。ここでのエラーの理由は文字列は変更不可(immutable)であることから来ている。一旦作成した文字列は変更してはいけない。上の例で最良の解決は新しい文字列を生成することだ:>>>greeting=’HelloWorld!’>>>new_greeting=’J’+greeting[1:]>>>printnew_greetingJelloWorld!この例では新しい文字とgreetingのスライスしたものが連結されている。これでは元々のgreetingには何も変更もない。8.6探索\u3000次ぎのプログラムは何をしているのだろうか?deffind(word,letter):index=0whileindex>>word=’banana’>>>new_word=word.upper()>>>printnew_wordBANANAドット記法の形式はメッソドupperとそのメッソドを適用する文字列、wordを特定している。空の丸括弧はこのメソッドが引数を何も取らないことを示している。メソッドの呼び出しは発動(invocation)と呼ばれる。今の場合はwordに対してupperを発動させたと言えばよい。明らかになるように、文字列に対するfindというメソッドは関数として書いたfindと極めて類似した処理をするものである:>>>word=’banana’>>>index=word.find(’a’)>>>printindex1この例ではwordに対してfindを探索する文字を引数として渡して発動している。メソッドfindは実際にはもっと複雑な処理ができる。引数は一文字でなく、文字列で構わない。>>>word.find(’na’)2また、第二の引数を付けることも可能で、この引数は探索を開始するインデックスを意味している:>>>word.find(’na’,3)4更に、第三の引数も取れる。これは探索を止めるインデックスを意味している:>>>name.find(’b’,1,2)-1この探索は失敗に終わっている。なぜなら、探索区間の1から2まで(2は含まれない)の間に文字’b’はないからである。\x0c'}, {'id': 'f24f85f0-1982-4399-8460-8f27276dbe85', 'page': 114, 'text': '114第8章文字列練習問題8.7前節に作成したcountと類似した機能を持つメソッドcountがある。このメソッドのドキュメントをよく読み、文字列’banana’に対して発動させ文字列に含まれる’a’の個数を表示せよ。練習問題8.8文字列に対する各種のメソッドについて、http://python.org/lib\\string-method.htmlをよく読み理解を深めよ。stripやreplaceの使い方を実際にやってみることは有益かもしれない。ドキュメンテーションは若干混乱するかもしれない構文規則の表現になっているかもしれない。例えばfindこうだ:find(sub[,start[,end]])角括弧は選択項目である。だから、subは必修だが、startは選択項目であり、startを選択してもendは選択項目である。8.9in演算子英単語inの表式で表現される演算子は二つの文字列を比較し、最初の文字列が第二の文字列に含まれているときにはTrueを返すブール代数演算子である:>>>’a’in’banana’True>>>’seed’in’banana’Falseこの演算子の例として、二つの文字列に共通に含まれている文字を表示する関数を示す:defin_both(word1,word2):forletterinword1:ifletterinword2:printletter変数の名前をうまく取っておくと、Pythonのプログラムは英文を読むように読める。このforループは「第一の文字列になかにある各文字が第二の文字列の中にあるときにはその文字をprintする」と読める。この関数を’apples’と’oranges’に適用すると以下のようになる:\x0c'}, {'id': '9920ec78-584d-4b97-94bd-96a21dae65f9', 'page': 115, 'text': '8.10.文字列の比較115>>>in_both(’apples’,’oranges’)aes8.10文字列の比較関係演算子は文字列にも適用できる。二つの文字列が一致しているかどうかを調べるにはifword==’banana’:print’Allright,bananas.’他の関係演算子もアルファベット順に単語を並べるのに有効だ:ifword<’banana’:print’Yourword,’+word+’,comesbeforebanana.’elifword>’banana’:print’Yourword,’+word+’,comesafterbanana.’else:print’Allright,bananas.’Pythonは大文字、小文字の扱いを普通人々がするようには処理しない。大文字で始まる全ての単語は小文字で始まる単語の前にくる。Pineappleとbananaを比較すると以下のよな表示になる:Yourword,Pineapple,comesbeforebanana.これに対処する一般的な方法は文字列を標準化された形、例えば全てを小文字に変換した後に比較することである。手榴弾(Pineapple)で武装した人に対処しなければならないときのためにこのことを記憶しておくとよい。8.11デバッギング配列の横断的な処理をするとき、その処理の初めと終わりを正しく選ぶことには注意が必要だ。以下は二つ単語を調べ、一つが他の逆順に並べたものであるときにはTrueを戻す関数の一例である。しかし、二つのエラーを含んでいる:\x0c'}, {'id': '210ebc81-ce9e-4d65-93bc-661ace5a522f', 'page': 116, 'text': '116第8章文字列defis_reverse(word1,word2):iflen(word1)!=len(word2):returnFalsei=0j=len(word2)whilej>0:ifword1[i]!=word2[j]:returnFalsei=i+1j=j-1returnTrue最初のif文は二つの単語の長さが等しいかどうか調べている。そうでなければFalseを戻り値として関数は終了する。従ってこれ以降は二つの単語の長さは同じとして処理できる。これは6.8節で議論した保護回路の一例である。変数i、jはインデックスである。iはword1を前方、jはword2を後方に横断的にサーベイする。二つの文字が同じでないと、その場でFalseを戻り値として関数は終了する。ループの全過程を終了して抜けてきた場合はTrueを戻り値として関数は終了する。単語potsとstopを使ってこの関数をテストする。Trueが期待されるが、インデックスが領域外というIndexErrorが出る:>>>is_reverse(’pots’,’stop’)File"is_reverse.py",line9,inis_reverseifword1[i]!=word2[j]:IndexError:stringindexoutofrangeこのようなときに私が最初にやることは、このエラーが発生する直前でこれらの二つのインデックスの値を表示させてみることである。whilej>0:printi,j#printhereifword1[i]!=word2[j]:returnFalsei=i+1j=j-1\x0c'}, {'id': '42b8ff05-dd9b-4f51-8488-c2de4c6c0e42', 'page': 117, 'text': '8.12.\n語句\n117\ni\n0\nj\n3\nword1\n’pots’\nword2\n’stop’\n図8.2: 状態図\n実行してみると、新たな情報が得られるはずだ:\n>>>is_reverse(’pots’, ’stop’)\n0 4...\nIndexError: string index out of range\nループの最初でj の値が4 になっているこのが、この値は文字列’pots’ では範囲\n外になる。この文字列の最後の文字を示すインデックスの値は3 で、j の最初の値\nはlen(word2)-1 である。\nそのように修正して、実行すると以下になる:\n>>>is_reverse(’pots’, ’stop’)\n0 3\n1 2\n2 1\nTrue\n今回の場合は正しい結果になったが、ループは三回しか回っていない。おかしい。\nこのようなときは、この関数の状態図を書いてみるとよい。図8.2 はis reverse\n関数の例で第一回目の繰り返し時の状態図である。私はフレーム内の変数が持つ値\nを表示する場所に少し工夫を施した。インデックスi、jが文字列word1、word2\nのどの文字を指しているかが分かるようにその値と文字を点線で結んだ。\n練習問題8.9 この図を紙に書いて繰り返し毎にインデックスi、j がどのような値\nをもつのか調べ、関数is reverse が持つ第二のエラーを修正せよ。\n8.12\n語句\nオブジェクト(object)\n:変数が参照するあるもの\n(訳注\n:\n定義に対してその実体)\n。\n配列(sequence)\n:整数インデックスによって同定される順序立った変数の組。.\nアイテム(item)\n:配列の一要素。\n'}, {'id': 'ddb05efc-57ef-4c07-81a0-157b4eb6d830', 'page': 118, 'text': '118\n第8 章\n文字列\nインデックス(index)\n:文字列のような配列内の要素を選択ために使う整数値。\nスライス(slice)\n:インデックスの範囲指定で取り出された文字列の一部。\n空文字列(empty string)\n:二つの引用符で表現された文字を含まない長さ0 の\n文字列。\n変更不可(immutable)\n:文字列の要素を代入できない性質。\n横断処理(traversal)\n:似たような操作を配列の端から端まで繰り返し行う。\n探索(search)\n:捜していたものが見つかったときに停止する横断処理の一形態。\nカウンタ(counter)\n:何か数えるために使われる変数。通常は0 で初期化され、\nその後インクリメントされる。\nメソッド(method)\n:一つのオブジェクトに付随しておりドット表記で呼ばれる\n関数。\n発動(invocation)\n:[メソッドを呼ぶ文。\n8.13\n練習問題\n練習問題8.10 文字列スライスは第三の引数を受け取ることができる。この第三の\n引数はスライスを何文字毎(ステップサイズ)にするかを指示するものである。ス\nテップサイズが2 であるということは、文字列内の文字を一つ置きに拾うことで\nあり、3 であると、三つ毎になる。\n>>> fruit = ’banana’\n>>> fruit[0:5:2]\n’bnn’\nこのステップサイズを-1 にすると後からのスライスになる。だからスライス[::-1]\nとするとその文字列の逆順の文字列が得られる。これを使って練習問題6.6 で作成\nした文字列が回文である調べる関数is palidrome の一行版を作成せよ。\n練習問題8.11 以下の関数はいずれも文字列が小文字を含んでいるかを調べる意図\nに作成したものであるが、いずれも問題を含んでいる。各々の関数が実際にして\nいることを述べよ(関数に文字列を渡すとして)\n。\n'}, {'id': '10422441-8253-4966-8b4a-604e04a7d253', 'page': 119, 'text': '8.13.\n練習問題\n119\ndef any_lowercase1(s):\nfor c in s:\nif c.islower():\nreturn True\nelse:\nreturn False\ndef any_lowercase2(s):\nfor c in s:\nif ’c’.islower():\nreturn ’True’\nelse:\nreturn ’False’\ndef any_lowercase3(s):\nfor c in s:\nflag = c.islower()\nreturn flag\ndef any_lowercase4(s):\nflag = False\nfor c in s:\nflag = flag or c.islower()\nreturn flag\ndef any_lowercase5(s):\nfor c in s:\nif not c.islower():\nreturn False\nreturn True\n練習問題8.12 ROT13 は各文字をアルファベットで13 文字分「回転」させる意味\nで暗号のかたちを取っている。文字を回転させるとはアルファベットのなかでずら\nし、文字Z を越えたときは先頭A に戻るように折り返す操作である。だから、文\n字’A’ を3だけ回転させると文字’D’ になり、’Z’ は1だけ回転させると’A’ になる。\n関数rotate word を作成せよ。この関数は文字列と整数を仮引数として持つ。\n文字列の各文字を整数で指定された量だけ「回転」させ、その結果の文字列を戻り\n'}, {'id': '658207b5-e2c9-4808-acca-89877d169909', 'page': 120, 'text': '120第8章文字列値とする関数である。例えば、“cheer”を7だけずらすと“jolly”になり、“melon”を10だけずらすと“cubed”になる。文字を文字コードに変換する組み込み関数ordや文字コードを文字に変換する組み込み関数chrを使ってもよい。インタネットに対する潜在的に悪意のあるジョークがROT13にコード化されていることがある。あまり気にならなければ、それを見つけ元に戻してみよう。\u3000解答例:http://thinkpython.com/code/rotate.py\x0c'}, {'id': '8740bd76-3b75-4d09-ad00-1a37dabe1af0', 'page': 121, 'text': '121第9章事例研究:単語あそび9.1単語リストの読み込みこの章の演習では英単語のリストが必要だ。英単語のリストはWebで入手可能なものが沢山あるが、我々の目的に最適なものはMobyレキシコンプロジェクト(http://wikipedia.org/wiki/Moby_Projectをみよ)の一部としてGradyWardによって収集され、公開されている英単語リストがその一つである。それは公式のクロスワードパズルや他の英単語ゲームに使える113,809個の英単語リストである。この集録は113809f.ficという名前のファイルになっているが、もっと簡単な名前、words.txtでhttp://thinkpython.com/code/words.txtからダウンロードできる。このファイルは単純なテキストファイルであるので、テキストエディタで閲覧できるし、Pythonで読むこともできる。組み込み関数openの引数ファイル名を与え実行するとファイルオブジェクト(fileobject)を返してくる。これを使ってファイルを読むことができる:>>>fin=open(’words.txt’)>>>printfin変数finは入力に使うファイルオブジェクトによく使われる名前である。モード’r’は読み込みモードでファイルがオープンされたことを示す(その逆は書き込みモードで’w’である)。ファイルオブジェクトは読み込みのためのいくつかのメソッドを提供している。その一つはreadlineでこれは文字を改行記号に達すまで読み込み、その結果を文字列として返すメソッドである:>>>fin.readline()’aa\\r\\n’このリストの第一番目の単語は“aa”である。これは溶岩の一種である。符号\\r\\nは二つ特殊記号、キャリージ・リターンと改行でこの単語を次ぎのものから分離\x0c'}, {'id': '04a1596c-6e2a-476c-8544-df15aef94513', 'page': 122, 'text': '122第9章事例研究:単語あそびするのに使われている。ファイルオブジェクトは今ファイルのどこにいるのかの軌跡を保存している。だから、次ぎにreadlineを実行すると次ぎの単語を読み出せる:>>>fin.readline()’aah\\r\\n’次の単語は“aah”で、全く規則にあった単語である。そんなに怪訝な様子で私をみないでくきださい。二つの特殊記号が邪魔ならば、文字列メッソドstripを使って取ってしまうこともできる:>>>line=fin.readline()>>>word=line.strip()>>>printwordaahedforループの一部にファイルオブジェクトを使うこともできる。以下のプログラムはwords.txtを読み込み、一行に一単語毎に表示するものである:fin=open(’words.txt’)forlineinfin:word=line.strip()printword練習問題9.1ファイルwords.txtを読み、単語の長さが20(特殊文字を含めないで)以上ある単語のみ表示するプログラムを作成せよ。9.2練習問題これからの練習問題の解答は次の節にあるが、答えをみる前に一度は解答を試みてほしい。練習問題9.2ErnstVincentWrightは1939年にGadsbyというタイトルの50,000単語の小説を出版した。この本は文字’e’を全く含んでいない。文字’e’は英語では最も頻度の高い文字であるので、この作業は簡単ではない。Infact,itisdifficulttoconstructasolitarythoughtwithoutusingthatmostcommonsymbol,itisslowgoingatfirst,butcautionandhoursoftrainingcangraduallygainsfacility.\x0c'}, {'id': '539b6de4-d71c-44c5-aa06-9592f5266939', 'page': 123, 'text': '9.3.\n探索\n123\nこの位で止めておこう。\n与えられた単語が文字’e’ を一つも含んでいないとTrue を返す関数has no e を\n作成せよ。\n前節のプログラムを単語が’e’ を含んでいないときにのみ単語を表示するように\n変更し、文字’e’ を含まない単語の全単語中に占めるパーセントを計算しなさい。\n練習問題9.3 文字列と文字を引数とする関数avoids を作成せよ。この関数はその\n文字列に該当する文字が一つも含まれていないときはTrue を返す。\nユーザから入力された文字に対して、この文字を一つも含まない単語を表示す\nるように前節のプログラムを変更せよ。\nまた、このような文字5 個を使ったときに排除される単語数が最少になるよう\nな5 個の組み合わせを見つけることができるか?\n練習問題9.4 文字列と文字の組み合わせ(文字列)を引数とする関数uses only\nを作成せよ。この関数はこの文字列がこの組み合わせ文字でのみ構成されている\nときはTrue を返す。また、組み合わせ文字、“acefhlo”のみを使って一つの文を作\nれるかな?“Hoe alfalfa”(アルファルファを刈り取る)以外には?\n練習問題9.5 文字列と要求される文字の組み合わせ(文字列)を引数とする関数\nuses all を作成せよ。この関数はこの文字列が要求される文字を全て少なくとも\n一回は使っているときはTrue を返す関数である。全ての母音“aeiou”を使うよう\nな単語は何個位あるのだろうか“aeiouy”ではどうかな?\n練習問題9.6 引数として受け取った単語の中の文字の並びがアルファベット順(同\nじ文字が並ぶのはよいとする)\nに並んでいるときTrue を返す関数is abecedarian\nを作成せよ。この条件を満たす単語は何個位あるのだろうか?\n9.3\n探索\n前節で取り上げた練習問題全ては共通した要素を持っている。それは8.6 節でみ\nた探索パターンで解答できることである。最も簡単な例は以下である:\ndef has_no_e(word):\nfor letter in word:\nif letter == ’e’:\nreturn False\nreturn True\n'}, {'id': '57643d5b-ed39-4949-ba3d-ccfaf43d2c4e', 'page': 124, 'text': '124\n第9 章\n事例研究:単語あそび\nこのfor ループは単語中の文字を横断的に探索する。もし文字“e”が見つかれば直\nちにFalse を返し関数は終了する。そうでなければ次ぎの文字に移る。ループを\n通常の姿で抜けたことは文字“e”を一つも含まないことなのでTrue を返す。\n関数avoids は関数has no e を一般化したものであるが、プログラムの構造は\n同じだ:\ndef avoids(word, forbidden):\nfor letter in word:\nif letter in forbidden:\nreturn False\nreturn True\n文字の組み合わせ文字列forbidden 内の文字を一つでも含んでいると直ちにFalse\nを返し関数は終了する。ループを通常の姿で抜けるとTrue を返す。\n関数uses only も条件は逆だが、同じ構造をしている。\ndef uses_only(word, available):\nfor letter in word:\nif letter not in available:\nreturn False\nreturn True\n禁止文字の組み合わせの替わりに、利用可能な文字の組み合わせを使う。利用可\n能文字以外の文字がみつかったら直ちにFalse を返して関数は終了する。\n関数uses all では単語と組み合わせ文字の役割を逆にする。\ndef uses_all(word, required):\nfor letter in required:\nif letter not in word:\nreturn False\nreturn True\n単語(word)の中の文字を横断的に調べる替わりに、要求された文字の組み合わ\nせ(required)の中の文字を横断的に調べる。要求された文字が一つでも欠けて\nいたら直ちにFalse を返し関数を終了する。\n本物のコンピュータ科学者のように考えるのであれば、uses all は前の解いた\n問題の新たな例証にすぎないことを認識するはずだ。そして以下のように書くだ\nろう:\n'}, {'id': 'aee02ba8-c6b1-4dd5-9a5e-84883a6cd60c', 'page': 125, 'text': '9.4.\nインデックス付きループ\n125\ndef uses_all(word, required):\nreturn uses_only(required, word)\nこの例は問題認識(problem recognition)と言われるプログラム開発法の一\n例である。そこでは当該の問題がそれまで解決した問題の新たな例証にすぎない\nことを認識し、既に開発した解法を適用する。\n9.4\nインデックス付きループ\n前節のプログラムではfor ループを伴う関数を作成した。文字列の中の任意の位\n置にある文字なので、そのループはインデックスが付くものではなかった。\n関数is abecedarian では隣接する文字との比較が入るので、for ループは少し\n注意が必要だ。\ndef is_abecedarian(word):\nprevious = word[0]\nfor c in word:\nif c < previous:\nreturn False\nprevious = c\nreturn True\n再帰関数を使う版は以下のようだ:\ndef is_abecedarian(word):\nif len(word) <= 1:\nreturn True\nif word[0] > word[1]:\nreturn False\nreturn is_abecedarian(word[1:])\nwhile ループを使う版は以下のようだ:\ndef is_abecedarian(word):\ni = 0\nwhile i < len(word)-1:\nif word[i+1] < word[i]:\nreturn False\ni = i + 1\nreturn True\n'}, {'id': '359cef21-f04f-47cd-bd68-6a00906fb7a8', 'page': 126, 'text': '126\n第9 章\n事例研究:単語あそび\nこのループはi=0 から始まり、i=len(word)-1 で終わる。ループを回る毎に第i\n番目の文字(これが現在の文字)と第i+1 番目の文字(これが次ぎの文字)とが\n比較される。このループを失敗なしで通過すると問題の単語はテストを合格した\nことになる。この処理が正しいことを確認するために、“flossy”という単語を例に\n手作業をしてみる。この単語の長さは6 である。従ってこのループを最後に回る\nときのi の値は4 である。この値は後から二番目の文字を示す。この最後のループ\nではこの後から二番目の文字と最後の文字を比較することになる。\n以下は関数is palindrome(6.6 節参照のこと)を二つのインデックスを使う版\nである:\ndef is_palindrome(word):\ni = 0\nj = len(word)-1\nwhile i>>cheeses=[’Cheddar’,’Edam’,’Gouda’]>>>numbers=[17,123]>>>empty=[]>>>printcheeses,numbers,empty[’Cheddar’,’Edam’,’Gouda’][17,123][]10.2リストは変更可能リストの要素にアクセスする構文は文字列の中の文字にアクセスするときと同じである。つまり、角括弧を使う。角括弧の中の表式はインデックスを表現する。インデックスは0から始まることに注意してほしい。\x0c'}, {'id': 'e7b0b4cd-416f-4370-b823-232853461dfc', 'page': 132, 'text': '132第10章リスト>>>printcheeses[0]Cheddar文字列と異なり、リストは変更可能である。代入文で角括弧の演算が左に現れるとそれはリストのある要素への代入を表現する:>>>numbers=[17,123]>>>numbers[1]=5>>>printnumbers[17,5]リストnumbersの第一要素は123だったものが5になった。リストはインデックスと要素との間にある関係と考えることもできる。この関係はマッピング(mapping)とも呼ばれている。つまり各インデックスは要素の一つを「写像」しているとみることができる。図10.1はリストcheeses,numbers,emptyの状態図を示している。これらのリストは箱によって表現されている。そ01listnumbers171235listempty012’Cheddar’’Edam’’Gouda’listcheeses図10.1:状態図の箱の外側にはリストの名前が、内側にはその要素が書かれている。cheesesは要素が0,1,2のインデックスを持つ一つリストを参照している。numbersは二つの要素を持つが、図はその第二要素は123から5に変更されたことを示している。emptyは空リストを参照している。リストのインデックスは文字列のそれと同じように働く。つまり•任意の整数表式がインデックスとして使える。•もし存在しない要素を呼び出したり、書き出したりするとIndexErrorになる。\x0c'}, {'id': '58beb47a-6089-4a44-87d9-e5a97b3bc8b5', 'page': 133, 'text': '10.3.リストの横断的処理133•もしもインデックスの値が負であるとすると、それはリストの後の要素からカウントしている意味だ。in演算子もリストにそのまま機能する。>>>cheeses=[’Cheddar’,’Edam’,’Gouda’]>>>’Edam’incheesesTrue>>>’Brie’incheesesFalse10.3リストの横断的処理リストの要素を横断的にアクセスする一般的な方法はforループを使うものである。構文は文字列の場合と同じである:>>>forcheeseincheeses:printcheeseこれはリストの要素を読み出すときのみの場合である。要素の書き込みや更新をしたいときにはインデックスが必要になる。この場合の一般的な方法はrange関数を使うものである:>>>foriinrange(len(numbers)):numbers[i]=numbers[i]*2このループでリストの各要素を横断的に更新できる。関数lenでリストの要素の数を出し、関数rangeで0からn-1までの数のリストを生成する。ここでnは問題のリストの長さである。ループが回る毎にiは次ぎの要素に対応するインデックスの値を持つ。代入文ではiは古い要素に値を読むことに使われ、新しい値を代入することにも使われている。空リストに対するforループでは本体は一度も実行されない:forxin[]:print’Thisneverhappens.’リストの中にリストを含ませることもできるが、そのリストは一つの要素としてカウントされる。従って以下のリストの要素の数は4である。[’spam’,1,[’Brie’,’Boquefort’,’PoileVeq’],[1,2,3]]\x0c'}, {'id': 'a1978939-7fd8-419e-9292-c68c106c087f', 'page': 134, 'text': '134第10章リスト10.4リストに対する演算演算子+はリストの連結を実現する。>>>a=[1,2,3]>>>b=[4,5,6]>>>c=a+b>>>printc[1,2,3,4,5,6]同じように演算子*は与えられた回数だけ繰り返したリストを生成する。>>>[0]*4[0,0,0,0]>>>[1,2,3]*3[1,2,3,1,2,3,1,2,3]最初の例では[0]を4回繰り返す。二番目では[1,2,3]を3回繰り返す。10.5リストのスライススライス演算もリストに適用できる:>>>t=[’a’,’b’,’c’,’d’,’e’,’f’]>>>t[1:3][’b’,’c’]>>>t[:4][’a’,’b’,’c’,’d’]>>>t[3:][’d’,’e’,’f’]一番目のインデックスを省略すると、スライスは先頭から始まる。二番目のインデックスを省略するとスライスは最後尾まで進行する。両インデックスを省略するとリスト全体がコピーされる。>>>t[:][’a’,’b’,’c’,’d’,’e’,’f’]リストは更新可能だから、折り畳み、回転、変更の操作をする前にそのコピーを作っておくことは多々有益である。スライス演算子を代入文の左辺で使うと、複数要素を更新できる:\x0c'}, {'id': 'eb76c287-6c3a-4bee-a53f-ee3591d425a5', 'page': 135, 'text': '10.6.リストメソッド135>>>t=[’a’,’b’,’c’,’d’,’e’,’f’]>>>t[1:3]=[’x’,’y’]>>>printt[’a’,’x’,’y’,’d’,’e’,’f’]10.6リストメソッドPythonはリストに適用できる各種のメソッドを提供している。例えば、appendは要素の追加に使う:>>>t=[’a’,’b’,’c’]>>>t.append(’d’)>>>printt[’a’,’b’,’c’,’d’]メソッドextendはリストを引数として受け取り、その要素の全てをリストに追加する:>>>t1=[’a’,’b’,’c’]>>>t2=[’d’,’e’]>>>t1.extend(t2)>>>printt1[’a’,’b’,’c’,’d’,’e’]この場合リストt2の中味は変わらない。メソッドsortはリストの要素を低位から高位に順に並べ替える:>>>t=[’d’,’c’,’b’,’a’]>>>t.sort()>>>printt[’a’,’b’,’c’,’d’]リストのメソッドは全て戻り値がない、だからNoneを戻す。もし間違ってt=t.sort()とすると、期待はずれの結果になる。10.7写像・フィルタ・還元\u3000リストの要素を全て足そうと思ったら、以下のようにループを使うだろう:\x0c'}, {'id': '11169508-8d21-4fda-95ea-015c477991f5', 'page': 136, 'text': '136\n第10 章\nリスト\ndef add_all(t):\ntotal = 0\nfor x in t:\ntotal += x\nreturn x\n変数total は0 で初期化される。変数x はループを回る毎にリストt の要素を\n得る。演算子+=は変数の更新の操作の省略形である。累積代入文(augmented\nassignment statement)、total += x はtotal = total + x と等価である。\nループが進むに連れて、total は要素の和の累積になる。このような機能のため\nに用いられる変数はアキュームレイタ(accumulator)と呼ばれる。\n要素の全和を求めることは一般的なので、Python では組み込み関数sum を提供\nしている:\n>>> t = [1, 2, 3]\n>>> sum(t)\n6\n要素の列を一つの値にしてしまうような操作を還元(reduce)と呼ぶ。\n練習問題10.1 入れ子になっている整数の全要素の総和を求める関数nested sum\nを作成せよ。\nときとして他のリストを作成しつつ、リストを横断的に処理する必要がある。例\nえば\ndef capitalize_all(t):\nres = []\nfor s in t:\nres.append(s.capitalize())\nreturn res\n変数res は空リストとして初期化される。ループが回る毎にオリジナルのリスト\nの要素が処理されres に追加される。従って変数res は別な種類のアキュームレ\nイタとみなすことができる。関数capitalize all のような処理を写像(map)と\nいう。ここではあるリストの全要素に同一の処理(capitalize())を施すからで\nある。\n'}, {'id': '5c11dcc5-8597-4fae-be42-e26cbb1ce152', 'page': 137, 'text': '10.8.\n要素の削除\n137\n練習問題10.2 関数capitalize all を使って、入れ子の文字列リストを受け取り\n文字列の全ての語頭を大文字に写像する関数capitalize nested を作成せよ。\nもう一つの操作はリストの一部を選択して部分リストを作成するものである。例\nとして文字列のリストから大文字のみを含む要素を選択し、部分リストを作る問\n題を考える:\ndef only_upper(t):\nres = []\nfor s in t:\nif s.isupper():\nres.append(s)\nreturn res\nメソッドisupper は文字列が大文字のみを含んでいるときTrue を返す。\nこのような操作はリストの要素のなかで条件を満たすものだけを別なリストの\n要素にするからフィルタ(filter)と呼ばれる。\nリストに対する操作で最も一般的なものはこれらの写像\n・フィルタ・還元を組み\n合わせたものである。Python は組み込み関数map や“list comprehension”と呼ば\nれている演算操作を含め、よく知られたリスト操作の機能を豊富に提供している。\n練習問題10.3 整数値のリストを引数として受け取り、累積をリストとして返す関\n数を作成せよ。ここで累積リストとは、新しいリストのi 番目の要素はオリジナ\nルのリストの最初からi+1 個の要素の総和である。例えば、[1, 2, 3] の累積リ\nストは[1, 3, 6] である。\n10.8\n要素の削除\nリストの要素を削除する方法はいくつかある。要素番号を知っているのであれ\nば、pop を使うことができる:\n>>> t = [’a’, ’b’, ’c’]\n>>> x = t.pop(1)\n>>> print t\n[’a’, ’c’]\n>>> print x\nb\n'}, {'id': 'be197a17-f18e-4dfb-9004-3c64aaeb7fb1', 'page': 138, 'text': '138第10章リストメソッドpopはリストを修正し、削除した要素を返す。もしpopをインデックスなして使うと最後尾の要素を削除し、その要素を返す。要素を戻す必要がなければ、del演算子が使える:>>>t=[’a’,’b’,’c’]>>>delt[1]>>>printt[’a’,’c’]削除する要素を知っている(しかし、要素番号は不明)のであれば、メソッドremoveが使える:>>>t=[’a’,’b’,’c’]>>>t.remove(’b’)>>>printt[’a’,’c’]もし一つ以上の要素を一時に削除したいのであれば、スライス演算子で範囲を決め、del演算子を使えばよい:>>>t=[’a’,’b’,’c’,’d’,’e’,’f’]>>>delt[1:5]>>>printt[’a’,’f’]スライスは第一番目のインデックスの要素から第二番目の要素まで選択する。注意、二番目のインデックスの要素は含まれない。練習問題10.4リストを引数として受け取りそのリストの先頭と最後尾の要素を削除した新しいリストを返す関数middleを作成せよ。middle([1,2,3,4])は[2,3]を返すわけである。練習問題10.5リストを引数として受け取り、先頭と最後尾の要素を削除する修正をそのリストに施す関数chopを作成せよ。戻り値はNoneである。10.9リストと文字列文字列は文字の配列であり、リストは値の配列である。しかし、文字のリストは文字列とは異なる。文字を要素とするリストに文字列を変換するには関数listを使う:\x0c'}, {'id': '6c29a866-bf57-458f-abdb-6e9cb9a8da85', 'page': 139, 'text': '10.10.オブジェクトと値139>>>a=’spam’>>>t=list(a)>>>printt[’s’,’p’,’a’,’m’]listは組み込み関数であるので、変数名として避けるべきである。またl(エル)はあまりにも1と似ているので避け、tを用いた。関数listは文字列を文字に分解しリストにする。文字列を単語に分解するのはsplitを使う:>>>s=’piningforthefjords’>>>t=s.split()>>>printt[’pining’,’for’,’the’,’fjords’]このメソッドに引数として区切り文字(delimiter)を与えることができる。これで如何なる文字を使って単語に分割するかを指定できる。>>>s=’spam-spam-spam’>>>delimiter=’-’>>>s.split(delimiter)[’spam’,’spam’,’spam’]メソッドjoinはsplitの逆の操作だ。このメソッドは引数として文字列のリストを受け取り、要素の連結を作る。joinは文字列のメソッドで区切り文字に対して発動される。リストはこのメソッドの引数として与える:>>>t=[’pining’,’for’,’the’,’fjords’]>>>delimiter=’’>>>delimiter.join(t)’piningforthefjords’このばあいは空白を区切り文字として単語を接合する。区切り文字なして接合するには空文字’’を区切り文字とすればよい。10.10オブジェクトと値以下のような代入文を実行してみる:\x0c'}, {'id': '142c6b2b-d080-49ea-a78d-25aa57ca8531', 'page': 140, 'text': '140第10章リストab’banana’ab’banana’’banana’図10.2:.状態図>>>a=’banana’>>>b=’banana’変数a、bとも同じ文字列を参照している。しかし、この二つが同一の文字列を参照しているかどうかは不明である。図10.2に示したように二つの可能性がある。一つの可能性は、この二つは同じ値を持つが異なったオブジェクト(実体)(object)を参照しているとするものだ。他方は同一のオブジェクトを参照しているとするものである。これをチェックするにはis演算子を使ってみればよい:>>>a=’banana’>>>b=’banana’>>>aisbTrueこの例ではPythonは一つ文字列オブジェクトを生成し、変数a、bともこれを参照している。しかし、二つのリストでは、二つのオブジェクトが生成される:>>>a=[1,2,3]>>>b=[1,2,3]>>>aisbFalse従って状態図は図10.3のようになる。この場合二つのリストは同じ要素を持ってab[ 1, 2, 3 ][ 1, 2, 3 ]図10.3:状態図いるので等価(equivalent)であるという。しかし異なったオブジェクトであるので同一(identical)ではない。同一である二つのオブジェクトは等価であるが、等価である二つのオブジェクトは必ずしも同一ではない。これまで「オブジェクト(実体)」と「値」を交換可能な言葉として使ってきたが、もう少し厳密に言うとこうなる。オブジェクトは値を持つ。操作[1,2,3]\x0c'}, {'id': '4dc108d2-ad9a-4fd8-b0cf-06294a670472', 'page': 141, 'text': '10.11.別名参照141を行うとその要素が整数の値を持つリストオブジェクトが得られる。もしも他のリストが同じ値を持っていたとすると、2つのリストは同じ値を持つというが、同一のオブジェクトではない。10.11別名参照変数aは一つのオブジェクトを参照し、b=aという代入文を実行すると、二つの変数は同一のオブジェクトを参照することになる:>>>a=[1,2,3]>>>b=a>>>bisaTrue従って状態図は図10.4のようになる。一つの変数にオブジェクトを代入することab[ 1, 2, 3 ]図10.4:状態図は参照(reference)という。この例では一つのオブジェクトに対して二つ参照があることになる。一つのオブジェクトに対して二つ以上の参照があるとき、このオブジェクトは別名参照された(aliased)オブジェクトという。もしも別名参照されたオブジェクトが変更可能であるとすると、一つの別名に行った変更は他にも反映される:>>>b[0]=17>>>printa[17,2,3]この振る舞いは利用できるときがあるが、エラーを起こしやすい。変更可能オブジェクトにたいする別名は避ける方が安全だ。変更不可のオブジェクトでは問題は少ない。>>>a=’banana’>>>b=’banana’この例で変数a、bが同じ文字列を参照しているかどうかは殆ど問題にならない。\x0c'}, {'id': '3ec08b02-877c-4d1e-b078-286d6e8551b2', 'page': 142, 'text': '142\n第10 章\nリスト\n10.12\nリストを引数に使う\nリストを関数の引数として渡すと、その関数はそのリストの参照ができる。関数\nはこの仮引数としてのリストを変更するとこの関数の呼び手のリストも変更を受\nける。例えば、関数delete head はリストの先頭の要素を削除する関数としよう\n:\n>>> def delete_head(t):\ndel t[0]\nこの関数を使う:\n>>> letters = [’a’, ’b’, ’c’]\n>>> delete_head(letters)\n>>> print letters\n[’b’, ’c’]\n仮引数t と変数letters とは同一のリストの別名参照である。これらのスタック\n図は10.5 図のようになる。このリストは二つのフレームで共用されているので中\n0\n1\n2\n’a’\n’b’\n’c’\nlist\nt\nletters\ndelete_head\n\n図10.5: スタック図\n間に置いた。\nリストの変更を伴う操作と新たなリストを生成する操作を区別することは特別\nに重要だ。例えば、メソッドappend はリストの変更を伴うが、演算子+はあらた\nなリストの生成になる。\n>>> t1 = [1, 2]\n>>> t2 = t1.append(3)\n>>> print t1\n[1, 2, 3]\n>>> print t2\nNone\n>>> t4 = t1 + [4]\n>>> print t4\n[1, 2, 3, 4]\n'}, {'id': 'b771e4a7-c824-484f-b92a-09f601637ae1', 'page': 143, 'text': '10.13.デバッギング143リストの変更を期待する関数を作ろうとするときもこの相異に注意が必要だ。defbad_delete_head(t):t=t[1:]#間違いだスライスは新しいリストを生成する。しかし仮引数として渡された元のリストはそのままである。そこで新規リストを生成し、そのリストを戻り値とする関数を作成した。関数tailは以下のように先頭の要素以外をリストとして戻す関数である:>>>deftail(t):returnt[1:]これも元のリストは変化なしだ。使ってみる:>>>letters=[’a’,’b’,’c’]>>>rest=tail(letters)>>>printrest[’b’,’c’]10.13デバッギングリストや他の変更可能オブジェクトを不注意に使うとデバッグに時間をとられることになる。以下はよくある落とし穴とその回避法である。1.多くのリストに関するメソッドは引数のリストの変更を伴い、戻り値はNoneである。これに反して、文字列に関するメソッドは元の文字列は変更しないで、新たに文字列を生成する。だから、文字列の対するメソッドはword=word.strip()のつもりでリストにも以下のような記述をしたら、それは間違いである:t=t.sort()#間違いメソッドsortはNoneを返すので、tに対する次の操作で問題が発覚する。リストに対するメソッドや演算を行う前にこれらに関するドキュメンテーションを注意深く読み、インタラクティブ・モードで試してみるべきである。リストが他の配列(例えば文字列)と共有するメソッドや演算はdocs.python.org/lib/typeseq.htmlに文書化されている。また、変更可能な配列にのみ適用されるメソッドや演算はdocs.python.org/lib/typeseq-mutable.htmlに文書化されている。\x0c'}, {'id': 'b70e5bec-ab5d-4b4a-83e2-f544eeb66fe6', 'page': 144, 'text': '144第10章リスト2.慣用句を選択し、それに固執せよ。リストの対する問題は同じことをするのに多くの方法があることである。例えば、要素の削除はpop、remove、del、またはslice代入によっても実現できる。また、要素の追加はメソッドappendや演算子+で実現できる。tがリストでxが追加要素であると以下の書き方は正しい:t.append(x)t=t+[x]しかし、以下は正しくない:t.append([x])#間違い!t=t.append(x)#間違い!t=[x]#間違い!t=t+x#間違い!これらをインタラクティブ・モードで実行し、何が起こるかみてほしい。最後の例のみは実行時エラーを吐き出し、その他はエラーがでないが結果がおかしい。3.コピーを作成し、別名参照を避けよ。引数に与えたリストを変更してしまうsortのようなメソッドを使い、且つ元のリストを原型でほしいときには、そのオリジナルのコピーを取ればよい:orig=t[:]t.sort()組み込み関数sortedを使うのも一手である。この関数は戻り値として新たなリストを返す。10.14語句リスト(list):\u3000値の系列。要素(elements):リスト(または他の配列)の中の個々の値。アイテム(items)とも呼ぶ。指標(index):リストの一要素を指し示す整数値。\x0c'}, {'id': 'eedc2b3a-41cf-478d-99fc-9eb2e8385151', 'page': 145, 'text': '10.15.\n練習問題\n145\n入れ子のリスト(nested list)\n:他のリストの要素になっているリスト。\nリストの横断処理(list traversal)\n:リストの各要素を端から端まで順次に処理\nすること。\nマッピング(mapping)\n:一つの集合の各要素を他の集合の各要素に対応させる\n関係。一つのリストは指標から要素へのマッピングである。\nアキュームレイタ(accumulator)\n:ループの数え上げや単一の結果に累積する\n際に使う変数。.\n累積代入文(augmented assignment statement)\n:+=. のような演算子を使っ\nて変数の値を更新する代入。\n還元(reduce)\n:配列を横断的してその要素を単独の結果に累積させる処理パター\nン。\n写像(map)\n:配列を横断的して各要素に同一の命令を施す処理パターン。\nフィルタ(filter)\n:リストを横断してある条件を満たす要素だけを抽出する処理\nパターン。\nオブジェクト(実体)\n(object)\n:変数が参照する実体。オブジェクトは値とタイ\nプを持つ。\n等価(equivalent)\n:同じ値を持つこと。\n同一(identical)\n:同じオブジェクトを参照していること。それ故それらは等価\nである。\n参照(reference)\n:変数と値との間の対応関係。\n別名参照された(aliased)\n:二つ以上の変数が同一の一つのオブジェクトを参照\nしている状況。\n区切り文字(delimiter)\n:文字列を何処で区切るかを指示する文字または文字列。\n10.15\n練習問題\n練習問題10.6 リストを引数として受け取り、そのリストが昇順にソートされてい\nるとTrue を、それ以外はFalse を返す関数is sorted 作成せよ。関係演算子<、>\n等は使えるものとする。\n'}, {'id': '79299034-864a-42b7-9ebf-a9d7f83293dd', 'page': 146, 'text': '146\n第10 章\nリスト\n例を示すと、\nis sorted([1,2,3]) はTrue、is sorted([’b’,’a’]) はFalse を\n返す。\n練習問題10.7 二つの単語が一つの単語の文字の入れ替えで他方の単語になると\nき、二つの単語はアナグラムであると言う。二つの単語を引数として受け取りそ\nれらがアナグラムであるとTrue を返す関数is anagram を作成せよ。\n練習問題10.8 所謂誕生日パラドックスである。\n1. リストを受け取りその要素が重複しているときはTrueを返す関数has duplicated\nを作成せよ。この関数ではリストを変更してはいけない。\n2. クラスに23 名の学生がいる。二人が同じ誕生日になる機会はどの程度ある\nか?学生数23 人の誕生日(1 から366 まで)を区間とする乱数を作り調べる。\nヒント:モジュールrandom の中にある関数randint を使う。\nこの問題については以下を参照せよ:\nhttp://en.wikipedia.org/wiki/Birthday-patadox\n解答例:http://thinkpython.com/code/birthday.py\n練習問題10.9 リストを引数として受け取りそのリストの要素の重複をなくした新\nたなリストを返す関数remove duplicates を作成せよ。リストの中の要素の並び\n順は元の順序でなくてよい。\n練習問題10.10 単語ファイルwords.txt を読み、一単語を一要素するリストを生\n成する関数を作成せよ。\n二つの版、メソッドappend を使うものと演算子+を使いt=t+[x] とするものを\n作れ。実行時間を計測しどちらが長い時間かかるか調べよ。まだ何故か?実行時\n間の計測はtime モジュールの関数が使える。\n解答例:http://thinkpython.com/code/wordlist.py\n練習問題10.11 単語が単語リストの中にあるかチェ\nックしたい。in 演算子を使う\nことも考えられるが、この演算子は端から端へ一つずつ要素を調べて行くので時\n間がかかる。単語リストはアルファベット順になっているので、探索には二分法が\n使える。単語リストの中間から初めて、目的の単語がこの中間の単語より前にあ\nるかどうかを調べる。そうであれば、前半を同様な方法で調べる。そうでないと\nきは、後半を調べる。どちらであっても半分は探索をしないで済む。リストの単語\n数が113,809 個であるとき、17 ステップで単語を見つけることができるか、存在\n'}, {'id': '7d57b0b7-0193-4329-94ed-e5b37e42a097', 'page': 147, 'text': '10.15.練習問題147しないかが分かる。ソートされたリストと目的の値を引数として、もし目的の値がリストにあるときはTrueを返し、存在しないときはFalseを返す関数bisectを作れ。またはモジュールbisectのドキュメンテーションを読み、使ってみる。解答例:http://thinkpython.com/code/inlist.py練習問題10.12二つの単語がお互いに逆順であるとき二つは「逆順ペア」と呼ぶことにしよう。単語リストに存在する全ての「逆順ペア」を見つけるプログラムを書け。解答例:http://thinkpython.com/code/reverse_pair.py練習問題10.13二つの単語から構成する文字を互い違いに組み合わせると新しい単語は生成できるとき、この二つの単語は「咬み合っている」と呼ぶことにしよう。例えば、“shoe”と“cold”から“schooled”が作れる。1.「咬み合っている」単語ペア全てを見つけるプログラムを書け。ヒント:全てのペアを列挙するな。2.三つ方法で「咬み合っている」単語はあるだろうか?つまり、その単語の一番目の文字、二番目の文字、または三番目の文字から出発して、三番目毎に文字を抜き出し、三つの文字列を作るとこれらの全てが単語を作り出すような単語を見いだすことができるか?(訳注:逆にみると三つの単語があり、それらを構成する文字を一文字毎に交互に抜き出して並べると単語になるような三つの単語があるかという問題になる)出典:この練習問題はhttp://puzzlers.orgよりヒントを得た。解答例:http://thinkpython.com/code/interlock.py\x0c'}, {'id': 'a7c5948d-be8d-4bd6-be69-e2cdb7075393', 'page': 1, 'text': ''}, {'id': '975f63e4-89d1-4236-9967-be2953752ec2', 'page': 149, 'text': '149第11章辞書辞書(dictionary)はリストに似たものであるが、より一般的なものである。リストではインデックスは整数でなければならなかったが、辞書では(ほとんど)任意の型で構わない。辞書をインデックス(これをキー(keys)と呼ぶ)の集合と値の集合との写像であると考えてもよい。一つのキーと一つの値の纏まりをキーと値のペア(keys-valuepair)と呼ぶし、ときとしてアイテム(item)と呼ぶ。辞書の例として、英語からスペイン語への写像を示す辞書を作ってみよう。従って、キーも値も全て文字列である。関数dictはアイテムなして空の辞書を生成する。言うまでもないが、dictは組み込み関数の名前であるので変数名として避けるべきである。>>>eng2sp=dict()>>>printeng2sp{}波括弧は空の辞書を表している。この辞書にアイテムを追加するのは角括弧を使う:>>>eng2sp[’one’]=’uno’この行でキーを’one’として値を’uno’とするアイテムが生成されたことになる。print文で表示してみると、キーと値をコロンで区切ったキーと値のペアが確認できる:>>>printeng2sp{’one’:’uno’}この出力形式は入力形式でもある。例として三つのアイテムを持つ新しい辞書を生成してみよう:>>>eng2sp={’one’:’uno’,’two’:’dos’,’three’:’tres’}これをprint文で表示すると:\x0c'}, {'id': 'd5e39cd7-402d-4ab9-b3a7-e32a198d9e22', 'page': 150, 'text': '150第11章辞書>>>printeng2sp{’three’:’tres’,’two’:’dos’,’one’:’uno’}となりちょっと戸惑う。キーと値のペアの順序が異なっている。あなたのコンピュータで実行するとまたこれと異なっているかもしれない。アイテムの並びは予想できないのだ。しかし、辞書では整数でインデックスすることは決してないのでなにも問題はない。その替わり、キーが対応するアイテムの選択に使われる:>>>printeng2sp[’two’]dosキー’two’は常に値’dos’にマップされるので、アイテムの並ぶ順序はどうでもよい。キーが辞書の中にないと、例外エラーになる:>>>printeng2sp[’four’]KeyError:’four’関数lenも使えて、キーと値のペアの個数を返す。>>>len(eng2sp)3演算子inも使える。この演算ではキーだろうとしたものがその辞書にキーとしてあるかどうかのブール代数値を返す(値としてあることは充分でない)。>>>’one’ineng2spTrue>>>’uno’ineng2spFalse辞書に値として存在するかどうかを確認したいならば、メソッドvaluesを使うとよい:>>>vais=eng2sp.values()>>>’uno’invaisTrue\x0c'}, {'id': 'c6233e37-e744-4235-a74d-bb88e3253193', 'page': 151, 'text': '11.1.カウンタの集合として辞書を使う151演算子inはリストと辞書とでは異なったアルゴリズムを使っている。リストでは8.6節で議論した探索アルゴリズムを使っている。リストが長くなって行くに従ってそれに比例して探索時間が長くなる。辞書に対してはPythonはハッシュ表(hashtable)と呼ばれているアルゴリズムを使っている。このアルゴリズムはアイテムの数がどんなに増えてもほぼ同じ時間で演算子inを実行できる。ここでは如何にしてこれを可能にしているかは説明しないが、http://en.wikipedia.org/wiki/Hash_tableを読んでほしい。練習問題11.1ファイルwords.txtを読み込み、英単語をキーとする辞書を作成せよ。値を何にするかは問わない。そして演算子inを使ってある文字列がその辞書にあるかどうかを調べる関数を作成せよ。もし練習問題10.11をやっていれば、二分探索とリストに対する演算子inの実装の違いによる検索スピードを比較できる。11.1カウンタの集合として辞書を使う例えば文字列が与えられて、この文字列に使われている文字の頻度を調べたいとしよう。この問題を処理する方法はいくつもある:1.アルファベットに対応する26個の変数を用意する。そして、文字列を横断的に眺め、ある文字が現れたら対応する変数の値をインクリメントする。連鎖if文を使うことになるだろう。2.要素が26あるリストを作成する。文字列を横断的に眺め、出現した文字を整数値に変換する関数ordを使って整数値に変換し、これをインデックスとしてリストの要素の値をインクリメントする。3.文字をキーとしてカウンタを値とする辞書を生成する。文字列を横断的に眺め、現れた文字が初めてのものであると、この文字をキーとして値を1とする要素を辞書に追加する。既出のものならば、該当する要素の値をインクリメントする。どの方法を選択しても同じ結果が得られる。しかし、各々は異なった計算を実装したことになる。一つの実装(implementation)は一つの計算方法による実行である。ある計算方法は他のものより優秀である。例えば、辞書を使った実装の優位な点は、事\x0c'}, {'id': '29bc1a39-49da-43c2-99dd-4d06b3d16ab7', 'page': 152, 'text': '152第11章辞書前にどんな文字がその文字列に現れるか知る必要がないことであり、現れた文字のみについてカウンタを作ればよいことだ。defhistogram(s):d=dict()forcins:ifcnotind:d[c]=1else:d[c]+=1returnd関数名はヒストグラム(histogram)である。これは統計学的な言葉で頻度分布を意味している。関数の一行目は空の辞書の作成である。forループは文字列を横断的に眺める。ループが回る毎にもし文字cが辞書にないときには新たに文字cをキーとし、値を1とするアイテムを生成する。もしcが既出ならばこの文字をキーとする要素d[c]をインクリメントする。>>>h=histogram(’brontosaurus’)>>>printh{’a’:1,’b’:1,’o’:2,’n’:1,’s’:2,’r’:2,’u’:2,’t’:1}この頻度分布は文字’a’や’b’は一度だけ、文字’o’は二回などであることを示す。練習問題11.2辞書にはgetというメソッドがある。これは引数としてキーとその既定値を受けとる。そのキーが存在すればそのアイテムの値を戻すが、なければその既定値を戻す。>>>h=histogram(’a’)>>>printh{’a’,1}>>>h.get(’a’,0)1>>>h.get(’b’,0)0このgetを使って関数histogramをよりコンパクトに書け。関数の本体にあるif文を無くすことができるはずだ。\x0c'}, {'id': '47ee0afc-5e31-4f36-ba4d-80c1c0ed3eb5', 'page': 153, 'text': '11.2.\nループ処理と辞書\n153\n11.2\nループ処理と辞書\n辞書に対してfor 文を使うと、それは辞書のキーを横断的に眺めることになる。\n例として各キーと対応する値を表示する関数print hist を以下示す:\ndef print_hist(h):\nfor c in h:\nprint c, h[c]\n実行してみる:\n>>>h = histogram(’parrot’)\n>>>print_hist(h)\na 1\np 1\nr 2\nt 1\no 1\nここでもキーの並びには特別な順序はない。\n練習問題11.3 辞書のキーをリストとして返すメッソドにkeysがある。print hist\nを修正してキーがアルファベット順に表示されるようにせよ。\n11.3\n逆ルックアップ\n辞書d とキー\x03\nk が与えられたときそのキーに対応する値を見つけることは容易\nい、つまり、v = d(k) だ。この操作をルックアップ(look up)という。\nしかし、v が与えられ、これからk をみつけるとなると何が起こるだろうか?二\nつの問題がある。第一はv にマップされるキーは一つ以上あることである。アプリ\nケーションによるが、そのキーの一つを選択すれば済みのこともあれば、全ての\nキーをリストにする必要があるかもしれない。第二はこの逆ルックアップ\n(reverse\nlookup)になっている探索を容易く行う方法がないことである。\n以下は値を受け取りその値にマップされた最初のキーを返す関数の例である:\ndef reverse_lookup(d, v):\nfor k in d:\nif d[k] == v:\nreturn k\nraise ValueError\n'}, {'id': '259a722f-fece-46c7-8b62-88da634a2a9b', 'page': 154, 'text': '154\n第11 章\n辞書\nこの関数は探索パターンのもう一つの例であるが、これまでにないraise を使っ\nている。raise 文は例外を作りだす。今の場合はValueError を吐き出す。このエ\nラーは引数の値が問題を含んでいることを示すものである。つまり、ループが終\nわってしまったということは、その辞書にはv を持つ要素はないことになり、例\n外を送出する。\n以下は成功裡に終わった逆ルックアップの例である:\n>>>h = histogram(’parrot’)\n>>>k = reverse_lookup(h, 2)\n>>>print k\nr\nそして不首尾の終わり例:\n>>>k = reverse_lookup(h, 3)\nTraceback (most recent call last):\nFile "", line 1, in ?\nFile "", line 5, in reverse_lookup\nValueError\n例外の送出はPython のインタプリタが送出するものと同じである。つまり、ト\nレースバックとエラーメッセージを表示する。\n送出文はオプションとして詳細なエラーメッセージを受け取ることができる:\n>>>raise ValueError,’value does not appear in the dictionary’\nTraceback (most recent call last):\nFile "", line 1, in ?\nValueError: value does not appear in the dictionary\n逆ルックアップは順ルックアップに比較して鈍い。もしこれを頻繁に使ったり、辞\n書が大きかったりするときにはプログラムの実行はその影響を被る。\n練習問題11.4 関数reverse lookup は値v をマップする全てのキーをリストとし\nて戻す。\nこの関数を作成せよ。キーが一つもないときは空リストを返すものとする。\n11.4\n辞書とリスト\nリストは辞書の値として現れることができる。例えば、文字から頻度をマップ\nする辞書が与えられ、この逆引き辞書を作りたいとしよう。つまり、頻度から文\n'}, {'id': '0c0968bb-860a-4378-aee2-853843cd2716', 'page': 155, 'text': "# 11.4. 辞書とリスト\n\n文字をマップする辞書を作りたいわけである。いくつかの文字が同じ頻度を持つことがあり得るので、この逆引き辞書の値は文字のリストにするのが妥当である。\n\n```python\ndef invert_dict(d):\n inverse = dict()\n for key in d:\n val = d[key]\n if val not in inverse:\n inverse[val] = [key]\n else:\n inverse[val].append(key)\n```\n\nリストを毎回辞書 key に対してキーを割り当て、変数 val は対応する値を持つ。もしも val が辞書 inverse にないとき、その val をキーに一要素を持つリスト(これをシングルトンと呼ぶ)を値として新しい辞書要素を生成する。そうでなければ、この val はキーとして既にあるので、対応する値 inverse[val] に key を追加する。実行してみる:\n\n```python\nhist = histogram('parrot')\nprint(hist)\n# {'a': 1, 'p': 1, 'r': 2, 't': 1, 'o': 1}\ninverse = invert_dict(hist)\nprint(inverse)\n# {'a': ['p'], 'p': ['a'], 'r': ['t', 'o'], 2: ['r']}\n```\n\n### 図 11.1: 状態図\n\n図 11.1 には辞書 list と inverse の状態図を示す。辞書は上部に変数の型を示す dict のラベルがあり、箱の中にはキーと値のペアが影響として並べて並べた。もしも値が整然な形式であるときにはそれは箱の中に並べたが、それがリストであるときには別な箱を用意してその中に隠れて並べた。"}, {'id': '9dd5cd50-fe73-4607-97a8-37a3f2e28795', 'page': 156, 'text': '156\n第11 章\n辞書\nリストは辞書の値として取り得るが、キーとしては使えない。以下は間違った\nその例である:\n>>> t = [1, 2, 3]\n>>> d = dict()\n>>> d[t] = ’oops’\nTraceback (most recent call last):\nFile "", line 1, in ?\nTypeError: unhashable type: ’list’\n既に述べたように辞書はハッシュテーブルを使って実装される。その意味すると\nころは、キーはハッシュ可能(hashable)でなければならないということだ。\nハッシュ\n(hash)は値(任意の型の)を受け取り、整数を返す関数である。辞書\nはこの整数(ハッシュ値という)をキーと値のペアの保存やルックアップに使う。\n辞書のキーが変更不可である場合は問題ないが、キーがリストのような変更可\n能なものであるといろんなことが起こる。例えば、キーと値のペアを生成したと\nする。キーはハッシュされ、キーと値のペアは対応するところに保存される。そ\nこでキーを変更したとしよう。変更されたキーはハッシュされる。新たなハッシュ\n値により、キーと値のペアは別なとことに保存される。こうなると、同一のキー\nに対して二つのアイテムができるとか、キーを発見できなくなるなどの混乱が起\nこる。\nこのため、キーはハッシュ化されなければならなく、リストのような変更可能\nなものであってはいけない訳である。このような制限を回避する方法は次章で触\nれるタプルを使うことである。\n辞書自体は変更可能なのでキーには使えないが、値としては使える。\n練習問題11.3 辞書にたいするメソッドsetdefaut のドキュメンテーションを読\nみ、関数invert dict をコンパクトに書け。\n解答例:http://thinkpython.com/code/invert_dict.py\n11.5\nメモ\n6.7 節の関数fibonacci で引数の大きな数を与えれば与えるほど実行にかかる時\n間が長くなることに気が付く。さらに大きな数では計算時間が急激に増える。こ\nの理由を理解するために、引数n=4 でfibonacci を実行した場合を調べてみる。\nそれには図11.2 のような呼び出しグラフ(call graph)を作成してみるとよい。\n'}, {'id': 'bfafd5f7-8b24-43cc-bd5f-c3fe4adfc1a8', 'page': 157, 'text': '11.5.メモ157呼び出しグラフは関数フレームの集合を示す。そこでは呼びだされた関数とその関数を呼んだ関数が線で繋がれている。グラフの頂点は引数n=4のfibonacciで、これが引数n=3とn=2のfibinacciを呼んでいる。替わって、引数n=3のfibinacciは引数n=2とn=1を呼んでいる。等々である。このようなグラフからfibonacci(0)やfibonacci(1)が全体で呼ばれている回数を調べてみよう。これからこのfibonacciプログラムはこの問題の解答としては不充分であることが分かる。この欠点は引数が大きくなるほど強調される。この解決法の一つは既に計算し終えた値を辞書に保存し値の軌跡を残して置くことだ。このように計算した結果を後のために保存することをメモ(memo)と呼ぶ。fibonaccin4fibonaccin3fibonaccin2fibonaccin0fibonaccin1fibonaccin1fibonaccin2fibonaccin0fibonaccin1図11.2:呼び出しグラフ以下はそれを使った関数fibinacciの実装である:known={0:0,1:1}deffibonacci(n):ifninknown:returnknown[n]res=fibonacci(n-1)+fibonacci(n-2)known[n]=resreturnres大域変数knownは辞書で既に計算したフィボナッチ数を保存して置く。この辞書の初期値は0を0にマップ、1を1にマップさせるものである。関数fibinacciが呼ばれる度に辞書knownが検索される。結果があればそれを使う。なければ計算\x0c'}, {'id': '121ae64a-bd49-4887-a799-345e7dea17f4', 'page': 158, 'text': '158\n第11 章\n辞書\nしてその結果を辞書に残すと共に戻り値として返す。\n練習問題11.6 この新たなfibonacci関数を実行してみよ。\nさらに従来のfibinacci\n関数との計算時間を比較せよ。\n練習問題11.7 練習問題6.3 で扱ったAckermann 関数に対してメモ機能を付けたも\nのせよ。そして、このメモ機能があると大きな値の引数でも関数の評価が可能か\nどうかを確認せよ。\nヒント:なし。\n解答例:http://thinkpython.com/code/ackermann_memo.py\n11.6\n大域変数\n前節の例では変数known は関数の外で生成された。これは__main__と呼ばれる\n特別なフレームにこの変数が属していることを意味している。__main__に属する\n変数は大域変数(global variable)と呼ばれる。この変数は任意の関数からアク\nセスすることができるからだ。関数に属するローカル変数が関数の終了と共に消\n失するのと異なり、大域変数は一つの関数呼ばれ、次が呼ばれようと残る。\n大域変数としてよく使われるのがある条件が満たされたどうかを表示するフラ\nグ(flags)機能を担った変数だ。以下verbose というフラグを使い詳細表示を制\n御する例である:\nverbose = True\ndef example1():\nif verbose:\nprint ’Running examle1’\nもしこの大域変数の再代入をしようとすると予想外のことが起こる。以下はある\n関数が呼ばれたことがあるかを追跡することを意図したプログラムである。\nbeen_called = False\ndef example2():\nbeen_called = True\n#間違い\nこのプログラムを実行すると、大域変数の値が変化していないことに気が付くは\nずだ。問題は関数example2 では新たなローカル変数been called が作成された\n'}, {'id': '619e3378-d303-4b38-ae68-4518a667f4dc', 'page': 159, 'text': '11.6.\n大域変数\n159\nことにある。関数が終了するとこのローカル変数は消失し、大域変数の値は何も\n変化はない。\n関数の内部で大域変数へ再代入をしたいときは使う前に大域変数の宣言\n(declare)\nをする:\nbeen_called = False\ndef example2():\nglobal been_called\nbeen_called = True\nglobal 文はインタプリタに、\n「この関数では、been called と呼ばれるものは大\n域変数で、ローカル変数として生成したものでないよ」と伝える役目をしている。\n大域変数の更新についても同様だ:\ncount = 0\ndef example3():\ncount = count + 1\n#間違い\nこのまま実行すると以下のエラーが出る:\nUnboundLocalError: local variable ’count’ referenced before assignment\nPython は変数count をローカルとみなし、代入の前に参照していることでエラー\nを吐き出す。解決法はcount を大域変数として宣言することだ:\ndef example3():\nglobal count\ncount += 1\n大域変数が変更可能なものであると、宣言なしで変更できる:\nknown = {0:0, 1:1}\ndef example4()\nknown[2] = 1\n従って大域リストに対しては、要素の追加、削除、変更は宣言なしでできる。し\nかし、再代入では宣言が必要となる:\ndef example5():\nglobal known\nknown = dict()\n'}, {'id': 'a4b827c4-eeb2-489c-a1be-d95de5366fda', 'page': 160, 'text': '160第11章辞書11.7ロング整数関数fibonacci(50)を計算すると、以下のようになる:>>>fibinacci(50)12586269025L最後のLは結果がロング整数(long型)であることを示す(Python3ではlong型はなくなり、全てint型となった)。int型は限られた整数であるが、long型は任意の長さの整数が表現できるが大きくなるとメモリーと計算時間を消費する。数学的演算やモジュールmathの関数もそのままlong型へも適用できる。一般にint型で書いたプログラムは変更なしにlong型にも通用する。結果があまりにも大きな整数であるとPythonは自動的に結果をロング整数に変換する:>>>1000*10001000000>>>100000*10000010000000000L最初の例はint型、二番目はlong型の例である。練習問題11.8大きな整数の指数は暗号の公開キーの一般的なアルゴリズムの基礎である。RSAアルゴリズムに関するWikipedia(http://en.wikipedia.org/wiki/RSA)を読み、メッセージをコード化、またはデコード化する関数を書け。11.8デバッギング扱うデータ(データセット)の量が多くなるに従って否応なしに表示によるデバッグや手作業によるデータのチェックが必要になる。ここではそのようなデータが多いプログラムに対するデバッグ作業のいくつかの示唆を示す:入力のスケールダウン:もし可能なら、入力するデータ量を減らしてみる。例えば、テキストファイルを読むプログラムであるなら、最初の10行だけ読んでみる。または最少単位のデータを用意する。これはファイル自体を修正するか、もっといい方法はプログラムを修正して最初のn行だけ読むようにする。\x0c'}, {'id': 'c6f5280a-98ad-47f7-9981-82e8466fef7b', 'page': 161, 'text': '11.9.語句161それでもエラーがあるときには、そのエラーを明らかにできる更に小さいデータ量で実行してみる。エラーが修正できたら、データ量を徐々に多くしてみる。要約的把握や型の確認:データセットの全てを表示し、確認作業をする替わりに、例えば、辞書の要素の数や数のリストの総和といったデータの要約的な量を表示してみる。また、実行時のエラーは値が正しい型でないことからくることが多々ある。このような場合は単に値の型を表示してみることで済む場合がある。自己点検の書き込み:自己点検できるような機能をコードに書き込むことができる。例えば、数値のリストの要素の平均値を計算しているとしよう。この平均値はこのリストの最大要素の値より小さいはずであり、最小要素の値より大きいはずである。このような検証は結果が「不健全」であることを検出するから、「健全性の検証」と呼ばれている。二つの異なった方法で得られた結果を比較するという検証はそれらが一貫しているかどうかの検証になる。これは「一貫性の検証」と呼ばれている。出力を綺麗に表示:デバッグのための表示を綺麗の表示することはエラーの個所を特定することに役に立つ。その例を6.9節でみた。モジュールpprintのpprint関数は組み込み型をより人間に読みやすい形式で表示する。足場建設のために費やした時間はデバッギングで消費する時間を縮めることができるのだ。11.9語句辞書(dictionary):キーの集合から対応する値への写像。キーと値のペア(keys-valuepair):キーから値への写像に具体的な表現。アイテム(item):辞書におけるキーと値のペアの別名。キー(keys):辞書のキーと値のペアにおいて対の最初に現れるオブジェクト。値(values):辞書のキーと値のペアにおいて対の二番目に現れるオブジェクト。これはこれまで使っていた「値」よりもっと特定の状況での「値」の使い方である。実装(implementation):計算の実際の実行方法。\x0c'}, {'id': 'd7d64960-c624-401b-b215-e8b5428855f4', 'page': 162, 'text': '162\n第11 章\n辞書\nハッシュ表(hashtable)\n:Python の辞書を実装するためのアルゴリズム。\nハッシュ(hash)\n:キーの場所を計算するためにハッシュ表を用いること。\nハッシュ可能(hashable)\n:ハッシュ関数を持つデータ型。整数、浮動小数点型\nそして文字列のような変更不可の型はハッシュ可能であり、リストや辞書は\nそうでない。\nルックアップ(look up)\n:一つキーを受け取りそれに対応する値を探すという辞\n書における操作。.\n逆ルックアップ(reverse lookup)\n:一つの値を受け取りその値にマップしてい\nる一つまたはそれ以上のキーを探すという辞書の操作。\nシングルトン(singleton)\n:一つの要素のみを含むリスト(または他の配列)\n。\n呼び出しグラフ(call graph)\n:一つのプログラムの実行過程で生成される各フ\nレームで関数の呼び出し側と呼ばれた関数側とを矢印で結ぶグラフ表現。\nヒストグラム(histogram)\n:カウンターの集合。\nメモ(memo)\n:余計な計算を回避するために計算を終えた結果を保存して将来に\n備える。\n大域変数(global variable)\n:: 関数の外で定義される変数。その変数は他の関数\nからもアクセスできる。\nフラグ(flags)\n:条件が満たされているかどうかを表示するために用いられるブー\nリアン変数。\n宣言(declare)\n:global のように変数の属性をインタプリタに知らせる文。\n11.10\n練習問題\n練習問題11.9 練習問題10.8 ではリストを引数として受け取りそのリストの要素\nが重複しているときはTrue を返す関数has duplicates を作った。辞書を利用し\nてより高速でコンパクトな改訂版関数has duplicates を作成せよ。\n解答例:http://thinkpython.com/code/has_duplicates.py\n'}, {'id': 'bf587137-3473-4e92-af1f-2761e568ebe8', 'page': 163, 'text': '11.10.\n練習問題\n163\n練習問題11.10 二つの単語は一つを回転したら他方に一致するときこの二つの単\n語は「回転ペア」と呼ぶことにする。words.txt を読み込んで全ての「回転ペア」\n検出するプログラムを作成せよ(練習問題8.12 の関数rotate word を参照せよ)\n。\n解答例:http://thinkpython.com/code/rotate_pairs.py\n練習問題11.11 これもCarTalk のパズル名人から借用した。\u3000\n(http://www.cartalk.com/content/puzzler/transcripts/200717)\nDan O’leary という人物から送られてきたものだ。\n五文字からなる一音節の単語を思いついたが以下のような面白い性質\nを持っているというのだ。まず、先頭の文字を削除すると四文字の単\n語になるがこれが元の単語と同音異義語である。つまりこの二つの単\n語の発音は厳密に一致するのだ。さらに、この文字を元のところにも\nどして、二番目の文字を削除すると四文字の単語になるが、これも元\nの単語の同音異義語になる。問題は、この元の単語はなにかだ。\nさて、成り立たないが例を示す。単語wrack つまりW-R-A-C-K を例にとろう。こ\nの単語は“wrack with pain”(痛さに卒倒する)などに使う言葉だ。先頭の一文字\nを削除するとR-A-C-K になる。“Holy cow, did you see the rack on that buck! It\nmust have been a nine pointer!”(おやまあ、あの雄ジカの角を見たかい。あれは\nナインポインタに違いないよ)などにみるね。そしてこの言葉は完全な同音異義\n語である。さて、’w’ を元に戻し、二番目の文字を削除すると、“wack”が得られ\nる。これも実在する単語であるが、この言葉は前二者と同音異義語になっていな\nい。しかし、Dan やわれわれが知っている単語で少なくとも一つは条件を満たす単\n語がある。ある文字列が単語リストにあるかどうかを調べる目的で練習問題11.1\nの辞書を使うことができる。二つの単語は同音異義語であるかどうかの検定には\nCMU 発音辞典を使うことができる:\nhttp://www.spech.cs.cmu.edu/vgi-bin/cmudict または\nhttp://thinkpython.com/code/c06d.txt からダウンロードできる。\nまた、http://thinkpython.com/code/pronounce.py をダウインロードできる。\nこの中のread dictionary は発音辞典から単語とその単語の主要な発音をPython\nの辞書として戻す関数である。\nパズル名人のこの問題を解くプログラムを作成せよ。\n解答例:http://thinkpython.com/code/homophone.py\n'}, {'id': '071df14a-35bc-46f8-a451-23e76c5c6859', 'page': 1, 'text': ''}, {'id': 'e6fb15c9-a296-44d0-86e4-d481f1d4b950', 'page': 165, 'text': '165第12章タプル12.1タプルは変更不可タプル(tuple)は値の配列である。その値は任意の型でよい。また、整数でインデックス化されている。これらの点で、タプルはリストに似ている。大きな相異は、タプルは変更不可なことである。構文的にはタプルは値をカンマで区切った配列である:>>>t=’a’,’b’,’c’,’d’,’e’必ずしも必要でないが、タプルを括弧で括ることが普通である:>>>t=(’a’,’b’,’c’,’d’,’e’)一つの要素だけでタプルを生成するときには最後にカンマが必要だ:>>>t1=’a’,>>>type(t1)値を括弧で括ったものはタプルではない:>>>t2=(’a’)>>>type(t2)組み込み関数tupleを使って空のタプルを作ることができる。引数なしでそれを使う:>>>t=tuple()>>>printt()もしも引数が配列(文字列、リスト、タプル)であるとすると、結果はその配列要素を要素とするタプルが得られる:\x0c'}, {'id': '9585f58c-ecb4-4ec0-b087-14cbfd14a6c7', 'page': 166, 'text': '166第12章タプル>>>t=tuple(’lupins’)>>>printt(’l’,’u’,’p’,’i’,’n’,’s’)組み込み関数名tupleを変数として使うことは避けるべきである。リストに対して使った演算はタプルで有効だ。角括弧は要素のインデックスとして使える:>>>t=(’a’,’b’,’c’,’d’,’e’)>>>printt[0]aスライス演算も区間要素を選択するのに使える:>>>printt[1:3](’b’,’c’)しかし、タプルの要素を変更することはできない:>>>t[0]=’A’TypeError:’tuple’objectdoesnotsupportitemassignmentしかし別なタプルに置き換えることはできる:>>>t=(’A’,)+t[1:]>>>printt(’A’,’b’,’c’,’d’,’e’)12.2タプルの代入二つの変数の交換は有用なことが多々ある。伝統的な手法は一時的な変数を使うものである。変数aとbの交換を例にみよう:>>>temp=a>>>a=b>>>b=tempタプルの代入(tupleassignment)はもっとエレガントが手法を提供している:>>>a,b=b,a\x0c'}, {'id': '5b7e18a5-0218-46be-a855-711e1ca40d75', 'page': 167, 'text': '12.3.タプルを戻り値167左辺は変数のタプルであり、右辺は表式のタプルである。各要素が対応する変数に代入される。右辺の表式は代入する前に評価される。左辺の変数の個数は右辺の値の個数と一致している必要がある。>>>a,b=1,2,3ValueError:toomanyvaluestounpackもっと一般的には、右辺は任意に配列(文字列、リスト、タプル)でもよい。電子メールアドレスをユーザ名とドメイン名にsplitを用いて分離する例を示す:>>>addr=’monty@python.org’>>>uname,domain=addr.split(’@’)右辺は二つの要素を持つリストであり、先頭の要素はunameに代入され、二番目はdomainに代入される。>>>printunamemonty>>>printdomainpython.org12.3タプルを戻り値厳密にいうと関数の戻り値は一つの値のみであるが、それがタプルであると多数の値を戻り値にする効果がある。例を示す。二つの整数のわり算で商と余りを出す問題を考えよう。組み込み関数divmodを使うと二つ整数のわり算の商と余りをタプルとして戻してくれる。\u3000だから、この二つをタプルに代入することができる。>>>t=divmod(7,3)>>>printt(2,1)または要素毎の代入でもよい:>>>quot,rem=divmod(7,3)>>>printquot2>>>printrem1\x0c'}, {'id': 'a3d808ff-c67f-4e3a-baf4-8e2adc009b6b', 'page': 168, 'text': '168\n第12 章\nタプル\n以下は簡単な例である:\ndef min_max(t):\nreturn min(t), max(t)\nmin、max は配列の最大と最小の要素を見つける組み込み関数である。\n関数min max\nはその両方を計算し、それをタプルとして戻している。\n12.4\n可変長引数タプル\n関数は可変長引数を受け取りことができる。記号*で始まる仮引数は複数の引数\nを一つのタプルに纏める(gathers)。例えば、関数print all は任意の数の引数\nを受け取り、それらをprint する:\ndef print_all(*args):\nprint args\n纏め引数の名前は自由に取れるが伝統的にarg を使う。この関数が正確に動くこ\nとを確かめる:\n>>> print_all(1, 2.0, ’3’)\n(1, 2.0, ’3’)\n「纏める」の補語はばらす(scatter)である。値の配列があり、それを複数の\n引数を受け取る関数に通したいときは、記号*を使う。例として、divmod 関数は\n厳密に二つの引数を要求するので、一つのタプルではエラーになる:\n>>> t = (7, 3)\n>>> divmod(t)\nTypeError: divmod expected 2 arguments, got 1\nしかし、タプルをばらすと二つの引数になり正常に動く:\n>>> divmod(*t)\n(2, 1)\n練習問題12.1 組み込み関数の多くは可変長引数タプルを使っている。例を挙げれ\nば、min、max も任意の長さの引数を受け取ることができる。\n>>> max(1, 2, 3)\n3\n'}, {'id': 'dcfe0439-cb93-49ab-912b-351b1c4e3e5e', 'page': 169, 'text': '12.5.\nリストとタプル\n169\nしかし、関数sum はそうでない。\n>>> sum(1, 2, 3)\nTypeError: sum expected at most 2 arguments, got 3\nそこで任意の引数を受け取り、その総計を返す関数sum all を作成せよ。\n12.5\nリストとタプル\n組み込み関数zip は引数として二つ以上の配列を受け取り、それらの配列の各\n要素を一つずつ組みにして「纏めて」タプルとし、それらのリストを返す:\n>>> s = ’abc’\n>>> t = [0, 1, 2]\n>>> zip(s,t)\n[(’a’, 0), (’b’, 1), (’c’, 2)]\n結果はタプルのリストである。各タプルは文字列から一文字、リストから一要素\nの二つのペアの要素を持つ。もし二つの配列の長さが一致しないときは短い方に\n合わせる:\n>>> zip(’Anne’, ’Elk’)\n[(’A’, ’E’), (’n’, ’l’), (’n’, ’k’)]\n\u3000\nこのようなタプルを要素とするリストに対してはリストのfor を使った横断的\nな処理ではタプルへの代入を使うことができる:\n>>> t = [(’a’, 0), (’b’, 1), (’c’, 2)]\n>>> for letter, number in t:\nprint letter, number\nループは回る度にPythonはタプルを選択し、\nそのタプルの要素をletterとnumber\nに代入する。結果は以下のようになる:\na 0\nb 1\nc 2\n'}, {'id': 'eb79e900-444f-4e15-a99e-fa8d6b535d59', 'page': 170, 'text': '170\n第12 章\nタプル\nこれら関数zip, for 文, そしてタプルの代入を使うと二つ以上の配列を一時に横断\n的調べることができる仕掛けが作れる。関数has match を例に示す。この関数は\n二つの配列の同じインデックスの要素が少なくとも一つは一致するかどうかを調\nべるものである。\ndef has_match(t1, t2):\nfor x, y in zip(t1,t2):\nif x == y:\nreturn True\nreturn False\n配列を横断的に眺めると同時にその要素のインデックスも必要なときには、関\n数enumerate を使うとよい:\nfor index, element in enumerate(’abc’):\nprint index, element\nこのループの表示は以下のようになる:\n0 a\n1 b\n2 c\n12.6\n辞書とタプル\n辞書にはitems というメソッドがある。これは辞書のキー・値ペアをタプルと\nしたリストを返す関数である。\n>>> d = {’a’:0, ’b’:1, ’c’:2}\n>>> t = d.items()\n>>> print t\n[(’a’, 0), (’c’, 2), (’b’, 1)]\n辞書で予想できたように並びは順不同である(Python3 ではイテレータを返すが、\n大方の目的ではイテレータはリストのように振る舞う)\n。\n逆方向の操作もある。タプルのリストを新規辞書に変換する:\n>>> t = [(’a’, 0), (’c’, 2), (’b’, 1)]\n>>> d = dict(t)\n>>> print d\n{’a’: 0, ’c’: 2, ’b’: 1}\n'}, {'id': '3c6b10ec-96e2-44ec-a69b-08ecee776d73', 'page': 171, 'text': '12.6.辞書とタプル171この関数dictと関数zipを組み合わせると大変簡明な辞書生成の方法ができあがる:>>>d=dict(zip(’abc’,range(3)))>>>printd{’a’:0,’c’:2,’b’:1}辞書のメッソドupdateはタプルのリストを受け取り既存の辞書に追加する。メソッドitemsとタプルの代入そしてfor文を組み合わせると辞書のキー・値ペアを横断的に調べる仕掛けができる:forkey,valind.items():printval,key結果は以下である。0a2c1bタプルを辞書のキー(リストはこの目的には使えないので)に使うことがよくある方法である。例えば、ファーストネームとラストネームを対にして辞書のキーとして使い、電話番号を値とする辞書である。それらをfirst,last,numberとすると以下のように書くことができる:dictionary[last,first]=number角括弧内の表式はタプルである。このような辞書の横断的な処理にもタプルの代入が使える。forlast,firstindictionary:printfirst,last,dictionary[last,first]このループはタプルであるキーを横断的に眺める。各タプルの組みをlast,firstに代入し、それに対応する辞書の値をprintする。01’Cleese’’John’tuple図12.1:状態図\x0c'}, {'id': '54450c50-21e3-447a-9ac5-a762b243f344', 'page': 172, 'text': '172第12章タプル(’Cleese’, ’John’)’08700 100 222’’08700 100 222’’08700 100 222’’08700 100 222’’08700 100 222’(’Chapman’, ’Graham’)(’Idle’, ’Eric’)(’Jones’, ’Terry’)(’Gilliam’, ’Terry’)(’Palin’, ’Michael’)’08700 100 222’dict図12.2:状態図この辞書のキーのタプルに対する状態図を描く方法には二つある。詳しい版ではリストと同じようにインデックスとそれが示す要素を書く。例として、(Cleese,John)のタプルを図12.1に示した。もっと大きな図ではそのような細部は無くてもよいと思うだろう。電話帳の例を図12.2に示した。そこではPythonで使うタプルの表式そのものを使った。対応する電話番号はBBCの苦情窓口のものだ。電話しないでほしい。12.7タプルの比較比較演算子はタプルを初めてとしてリストにも適用できる。Pythonは初めの要素を比較する。等しいならば次ぎの要素を比較する。異なる要素に出会うまでこれを繰り返す。ここでの比較が結果である。だから以降の要素は無視される。>>>(0,1,2)<(0,3,4)True>>>(0,1,2000000)<(0,3,4)True関数sortも同じようにタプルのリストにも有効だ。\u3000最初は先頭の要素を比較し、同じならば次ぎの要素と繰り返し、違いがあればそれでソートする。このソートの特徴はDSUと呼ばれるプログラムパターンに繋がる。つまり、•デコレート(Decorate):ソートのキーを一つまたは二つ先頭に付けて、要素と対にしたタプルを作る、それをリストにしたものである。•そしてこのタプルのリストをソート(Sort)する。•アンデコレート(Undecorate):そしてできたタプルのリストをバラして要素のリストだけにする。これでソートされた要素だけのリストができる。\x0c'}, {'id': 'd7ff22a1-9fa8-4427-b869-57e48987f00c', 'page': 173, 'text': '12.8.配列の配列173単語の長さ順にソートされた単語を要素とするリストを生成するプログラムを示す:defsort_by_length(words):t=[]forwordinwords:t.append((len(word),word))t.sort(reverse=True)res=[]forlenght,wordint:res.append(word)returnres第一のループで単語とその単語の長さを先頭にしたタプルのリストを構築する。そして各タプルの最初の第一の要素、つまり長さでソートする。第二要素は第一要素が同じで決着をつけるときにのみ考慮される。ソートの引数reverse=Trueは長い順にソートするためである。第二のループはソートされた単語のみのリストを生成するためだ。練習問題12.2上の例では長さが同じばあいには単語はアルファベットの逆順に並ぶ。他の応用では、ここの単語の並びは乱雑であることがほしい場合がある。そこで、上のプログラムを修正して、同じ長さならばその単語は乱雑に並ぶようにせよ。ヒント:randomモジュールのrandom関数を参考にせよ。解答例:http://thinkpython.com/code/unstable_sort.py12.8配列の配列ここまではタプルのリストの話を進めてきたが、これらはリストのリスト、タプルのタプル、リストのタプルなどにも大凡適用できる。重複を避けるために配列の配列として話を纏めることにする。多くの状況で、異なった形式の配列(文字列、リスト、タプル)は相互に交換可能である。それではどのようにして、何故に他の形式より、ある配列形式を好ましいとして選択すべきだろうか?自明なことであるが、文字列は要素が文字のみであるということから他の形式の配列より制限がきつい。それらは変更不可でもある。もし文字の一部を変更し\x0c'}, {'id': '44a76a30-45d6-48b4-bcf7-b2a153691f82', 'page': 174, 'text': '174第12章タプルたいと思ったら、文字列の替わりに文字のリストを使うに違いない。リストは変更可能ということから、タプルより一般的と言える。しかし、タプルの方が好ましいと思われるいくつかの場合がある:1.例えばreturn文のようないくつかの状況では、リストよりタプルで処理する方が構文的に簡単になることがある。その他の状況ではリストが好ましいかもしれない。2.もし配列を辞書のキーとして使いたいなら、変更不可のタプルや文字列を使わざるをえない。3.もし配列を関数の引数に渡したい場合があるなら、リストによる別名処理の弊害を避けるためにタプルを使った方が安全だ。タプルは変更不可なので、既存のリストの変更を伴うsortやreverseなどのメソッドは存在しない。しかし、sortedやreversedなどの関数が用意されていて任意の配列を受け取り新しい配列を返すことができる。12.9デバッギングリスト、辞書、タプルは一般にデータ構造(datastructure)として知られているが、この章ではタプルのリスト、タプルをキーとしてリストを値とする辞書のような複合データ構造を初めてとりあげた。複合データ構造は有用だが、私が型エラー(shapeerrors)と名付けたエラーに陥りやすい。このエラーはデータ構造の間違った型や大きさ、複合の仕方が原因として起きる。例えば、一つの整数を要素とするリストを期待していたのに、普通の整数が与えられたとすると、動かなくなるといったものだ。この種のエラーをデバッグするために、structshapeというモジュールを作り、関数structshapeが使えるようにした。この関数は任意のデータ構造を引数として受け取りその型について纏めた文字列を返す関数である。ダウンロード先はhttp://thinkpython.com/code/structshape.pyである。以下いくつかの簡単な例を示す:>>>fromstructshapeimportstructshape>>>t=[1,2,3]>>>printstructshape(t)listof3int\x0c'}, {'id': 'a60b31c8-ab50-4246-b40f-5a57234613ca', 'page': 175, 'text': '12.10.語句175もっと凝ったプログラムでは“listof3ints”と書くかもしれない。しかし、複数形を斟酌しない方が簡単だ。リストのリストだと以下のようになる:>>>t2=[[1,2],[3,4],[5,6]]>>>printstructshape(t2)listof3listof2intもしも要素が異なった型であるとすると、structshapeは型毎に纏めて表示する:>>>t3=[1,2,3,4.0,’5’,’6’,[7],[8],9]>>>printstructshape(t3)listof(3int,float,2str,2listofint,int)タプルのリストでは:>>>a=’abc’>>>lt=zip(t,a)>>>printstructshape(lt)listof3tupleof(int,str)整数をキーとして文字列を値とする三つの要素を持つ辞書では>>>d=dict(lt)>>>printstructshape(d)dictof3int->strデータ構造の追跡で何か問題が起きたら、この関数structshapeが有効だ。12.10語句タプル(tuple):要素を変更できない配列。タプルの代入(tupleassignment):右側に一つの配列、左側に複数の変数のタプルを置く代入文。右側が評価され対応する要素(複数の値からなる)が左側のタプルに代入される。纏める(gathers):可変長引数タプルを纏める操作。ばらす(scatter):関数の引数(複数)を一つの配列として扱う操作。\x0c'}, {'id': '950ba870-2a37-4ebb-a2ea-f338404cd4a9', 'page': 176, 'text': '176第12章タプルデコレート・ソート・アンデコレート(DSU):“decorate-sort-undecorate,”の略称。タプルのリストを作り、それをソートして結果の部分を抽出する手順。データ構造(datastructure):リスト、辞書、そしてタプル等のかたちに纏められた値の集合。型エラー(shapeerrors):データ構造のような複雑に纏められたデータにアクセスする際に起こる型の不一致によるエラー。12.11練習問題練習問題12.3文字列を受け取りその文字列に含まれている文字の頻度を降順に表示するプログラムを作成せよ。色々な言語で書かれた文書を調べ文字の頻度分布が言語でどのように異なるかを調べ、以下の文献と比較せよ。http://en.wikipedia.org/wiki/Letter_frequencies.解答例:http://thinkpython.com/code/most_frequent.py練習問題12.4アナグラム再論!1.ファイルから単語集を読み込み(9.1節をみよ)、アナグラムになっている単語の全てを表示するプログラムを作成せよ。どんな出力になるのかを例で示す:[’deltas’,’desalt’,lasted’,’salted’,’slated’,’staled’][’retainers’,’ternaries’][’generating’,’greatening’][’resmelts’,’smelters’,’termless’]ヒント:文字のセットとそれらの文字から作れる単語のリストとを写像する辞書を作ることになるかもしれない。問題はいかに文字のセットをキーとする辞書を作るかである。2.前のプログラムを修正して最大の数の単語を含むアナグラムを最初に表示し、次ぎは二番目というような順序に表示にせよ。3.スクランブル(単語ゲーム)では、ボード上にある一文字とラックに用意された七個の文字タイルを全て使って八文字の長さの単語が作れると「ビンゴ」になる。どんな八文字のセットが最も「ビンゴ」になりやすいか?\x0c'}, {'id': '765e97fe-b61a-440c-8bea-5f01c7180be4', 'page': 177, 'text': '12.11.練習問題177ヒント:七つの単語が作れるものがある。解答例:http://thinkpython.com/code/anagram_sets.py練習問題12.5一つ単語の中にある二文字を入れ替えると他の単語になるときこの二つの単語は「字位転換ペア(metathesis)」という。辞書中の全ての字位転換ペアを見つけ表示するプログラムを作成せよ。ヒント:単語集の全てのペアを検証するな、そして、一つの単語の可能な交換の全てを検証するな。(訳注:その心は含まれて文字が同じもののみ、つまりアナグラムになっていて二個所で文字が異なる単語だ)。解答例:http://thinkpython.com/code/metathesis.py出典:この練習問題は以下の例題に啓発された:http://puzzler.org練習問題12.6これもCarTalkのパズル名人の問題である。(http://www.cartalk.com/content/puzzler/transcripts/200651)英単語があり、それを構成している文字を一回に一文字を削除して文字を詰めると新たな単語になるような最も長い単語はなにか?さて、文字の削除は先頭でも中程でもよいが、文字の並び替えはできない。文字を削除したら単に文字を詰めてみると他の英単語になっている。これが成功したら、その新しい単語から一文字削除し、詰めると英単語が現れる。最後は一文字だけになるがこれも辞書にある英単語である。このような英単語の中で最も長い単語はなにか、また何文字からなるか?これが知りたいことである。平凡で短い単語spriteで例を示す。spriteで始める。まず、文字rをとる、するとspiteが作られる。さらに、文字eをとると、spitができる。またさらに、文字sをとると、pitが生成され、文字pをとるとitが、さらに文字tをとるとIが出現する。最後のIも辞書にある単語だ。上の例のように最終的に一文字の英単語に縮小できる(全縮小可能と呼ぶ)全ての英単語を見つけるプログラムを作成せよ、そして、その中で最も長い単語をみつけよ。この練習問題は今までのものより難しいので、いくつか示唆を与えておこう。1.単語を引数として受け取り、それから一文字削除してできる単語(子ども)の全てをリストにして返す関数を生成してみる。2.ある単語はもしその子どもの一つでも全縮小可能ならば再帰的に全縮小可能である。そこでは空の文字列を再帰の基底ケースと考える。3.今まで使ってきたwords.txtには一文字の単語が含まれていないので、“I”、“a”と空文字をそれに加える。\x0c'}, {'id': '4c8af8f0-ee5c-4287-95eb-1ddc22ff1fb6', 'page': 178, 'text': '178第12章タプル4.プログラムの効率を高めるために全縮小可能単語を記憶しておくことが考えられる。解答例:http://thinkpython.com/code/reducible.py\x0c'}, {'id': '0b838ca6-d334-4c03-90c5-17be179558c8', 'page': 179, 'text': '179第13章事例研究:データ構造・選択13.1単語頻度分布解析いつものように解答例をみる前に、少なくとも解答を試みるようにしてほしい。練習問題13.1ファイルからテキストを読み、単語に分解し、区切り文字や句読点を取り除き且つ大文字を全て小文字に変換するプログラムを作成せよ。ヒント:モジュールstringは空白、タブ、改行文字等を含む文字の定義whitespaceや句読点を集めた文字の定義punctuationを提供している。確かめてみよう:>>>importstring>>>printstring.punctuation!"#$%&’()*+,-./:;<=>?@[\\]^_‘{|}~$削除せよさらに、文字列に対するメソッドstrip,replace,translateなども使える。練習問題13.2グーテンベルグ・プロジェクト(gutenbergproject)から好みの本をテキストベースでダウンロードしてPythonで読めるようにせよ。そして、使われている単語を表示してみる。異なった時代、異なった著者の異なった著書で得られた結果を比較せよ。どの著者が最も多くの語彙を使っているか?練習問題13.3前の練習問題を修正して取りあげた本で使われている最頻度度二十番目までの単語を表示せよ。練習問題13.4第9.1節の単語集(words.txt)を読み込み、取りあげた本で使っている単語でこの単語集にないものを表示せよ。誤植はいくつあるか?単語集に掲載すべき通常の単語はいくつあるか?はっきりしないものはいくつあるか?\x0c'}, {'id': '42953489-5b15-46db-bf0b-1711cecc5c2c', 'page': 180, 'text': '180\n第13 章\n事例研究:データ構造・選択\n13.2\n乱数\nある与えられた入力に対してコンピュータプログラムは常に同一の結果を出力\nする。この状況を決定論的(deterministic)と呼ぶ。われわれは常にこのような\n状況を期待しているので、決定論的な処理は良好だと思っている。しかし、ある\n種のアプリケーションではコンピュータに予測不可な振る舞いを期待する。ゲー\nムはその明白な例であるがそれ以外にもある。\nプログラムで真に非決定論的な振る舞いを実現することは易しくないが、非決定\n論的な振る舞いにみえるものを作るには方法がある。その一つが疑似乱数(pseu-\ndorandom number)を生成するアルゴリズムである。決定論的なプロセスで生\n成されるので、疑似乱数は真の意味では乱数ではないが、みた目には殆ど乱数と\n区別することは不可能だ。\nモジュールrandomは疑似乱数を生成する関数を提供している(これ以降単に\n「乱\n数」と呼ぶことにする)\n。関数random は区間0.0 から1.0 までの間の乱数(0.0\nは含み1.0 は含まない)を生成する。関数random を呼び出す毎に長い数列の中の\n次ぎの数が得られる。例をみるために以下を実行してみよう:\nimport random\nfor I in range(10):\nx = random.random()\nprint x\n関数randint は二つの引数low とhigh を受け取り、このlow とhigh(両方とも\n含む)の区間の整数乱数を生成する:\n>>> random.randint(5, 10)\n7\n>>> random.randint(5, 10)\n5\nモジュールrandom はガウス分布、指数分布、ガンマ分布などの連続分布関数に\n従って乱数を生成する関数を提供している。\n練習問題13.5 第11.1 節で定義された頻度分布を引数として受け取り、その頻度\n分布に従って値を選択し、その値を戻り値とする関数choose from hist を作成せ\nよ。例を示す:\n>>> t = [’a’, ’a’, ’b’]\n>>> hist = histogram(t)\n'}, {'id': 'fbcd5dff-5574-48b3-ae4b-96b1d6066ef1', 'page': 181, 'text': '13.3.\n単語ヒストグラム\n181\n>>> print hist\n{’a’:2, ’b’:1}\n従ってこの関数は’a’ を2/3 の確率、’b’ を1/3 の確率で選択しなければならない。\n13.3\n単語ヒストグラム\n前節の演習問題は自分で解答を試みてほしいが、解答例は以下にある。\nhttp://thinkpython.com/code/analyze_book.py\nこの解答例で使った文献も必要だ(http://thinkpython.com/code/emma.txt)\n。\n以下はファイルから読み込んだ文献中の単語のヒストグラムを作成するプログ\nラムである:\nimport string\ndef process_file(filename):\nhist = dict()\nfp = open(filename)\nfor line in fp:\nprocess_line(line, hist)\nreturn hist\ndef process_line(line, hist):\nline = line.replace(’-’,’ ’)\nfor word in line.split():\nword = word.strip(string.punctuation+string.whitespace)\nword = word.lower()\nhist[word] = hist.get(word, 0) + 1\nhist = process_file(’emma.txt’)\nこのプログラムではJane Austen 著“Emma”をプレーンテキストにしたファイル\nemma.txt を読み込む。関数process file 中のループはファイルから一行読みそ\nの都度それを関数process line に渡している。変数hist は加算機の役割をして\nいる。関数process line ではsplit を用いて単語に分解する前にreplace を用\nいて一行の文字列中に含まれるハイフンを空白に置き換えている(訳注:これは\n合成語の処理。ハイフネーションに対しては別の処理が必要)\n。その後に、単語の\n'}, {'id': '3da3a9db-b5cd-4e3e-9cb0-68fa81b2245e', 'page': 182, 'text': '182\n第13 章\n事例研究:データ構造・選択\nリストをstrip で句読点を削除、lower で全ての文字を小文字に変換する処理を\n横断的に行っている(文字列が「変換された」というのは速断である。文字列は\n変更不可である、だからstrip やlower では新たな文字列が生成されたのだ)\n。最\n後にprocess line は新たなアイテムを追加または既存のアイテムの値を1増加す\nるかして更新処理をする。\nファイル中の単語の総数は辞書の頻度の総和を求めることである:\ndef total_words(hist):\nreturn sum(hist.values())\n異なった単語の総数は辞書の中のアイテムの数である:\ndef different_words(hist):\nreturn len(hist)\n結果の表示の一例を以下に示す:\nprint ’Total number of words:’, total_words(hist)\nprint ’Number of different words:’, different_words(hist)\n結果は:\nTotal number of words: 161080\nNumber of different words: 7214\n13.4\n頻度の高い単語\n頻度の高い単語を検出するためには、DSU 処理を適用する。関数most common\nはヒストグラムを引数として受け取り、頻度で降順にソートし、頻度と単語をタ\nプルとするリストを返す。\ndef most_common(hist):\nt = []\nfor key, value in hist.items():\nt.append((value, key))\nt.sort(reverse=True)\nreturn t\n降順にソートされているので先頭の十個のアイテムを表示してみる:\n'}, {'id': 'e86ab213-904f-45ca-9f6f-3bd2f984ab6d', 'page': 183, 'text': '13.5.選択的な仮引数183t=most_common(hist)print’Themostcommonwordsare:’forfreq,wordint[0:10]:printword,’\\t’,freq#’\\t’はタブEmmaを解析した結果は以下のようになる:Themostcommonwordsare:to5242the5205and4897of4295i3191a3130it2529her2483was2400she236413.5選択的な仮引数これまで組み込み関数やメソッドの中に可変数の引数を受け取るものがあった。ユーザ定義関数でも選択的な仮引数を使うことでこれは可能だ。ヒストグラムから頻度の高い単語を表示する関数で例を示そう:defprint_most_common(hist,num=10):t=most_common(hist)print’Themostcommonwordsare:’forfreq,wordint[0:num]:printword,’\\t’,freq最初の仮引数は必須で、第二の仮引数は選択である。その仮引数numの既定値(defaultvalue)は10である。もし引数一つのみでこの関数を呼び出すと、つまり、print_most_common(hist)変数numの値はその既定値が使われる。もし二つの引数で呼べば、つまり\x0c'}, {'id': 'a52759dd-89f3-44ef-81c8-718fca799cff', 'page': 184, 'text': '184\n第13 章\n事例研究:データ構造・選択\nprint_most_common(hist, 20)\n変数num の値は引数の値が使われる。換言すれば、選択的な引数を与えることは\n既定値を無効にする(overrides)わけである。\n関数が必須引数と選択的な引数で構成されているときには、全ての必須引数を\nまず並べ、選択的な引数はそれに続けて並べる。\n13.6\n辞書の差し引き\nある本で見つかった単語で単語集words.txt に存在しない単語を探したいとい\nう問題は一つの集合で他の集合にない全ての要素を見つける集合の差の問題であ\nる。関数substract は二つの辞書d1 とd2 を引数として受け取り、d2 にないd1\nの要素を新たな辞書として返す関数である(新たな辞書はキーが重要で値は全て\nNone とする)\n。\ndef substract(d1, d2):\nres = dict()\nfor key in d1:\nif key not in d2:\nres[key] = None\nreturn res\n辞書d2 としてwords.txt を選びprocess file で辞書化して使う:\nwords = process_file(’words.txt’)\ndiff = substract(hist, words)\nprint "The word in the book that aren’t in the word list are:"\nfor word in diff.keys():\nprint word\n小説Emma の結果は以下のようになる:\nThe word in the book that aren’t in the word list are:\nrencontre \u3000genlis \u3000jane’s \u3000blanche \u3000woodhouses \u3000disingenuousness\nfriend’s \u3000Venice \u3000apartment… … .\nいくつかは名前や所有格であり、\n“rencontre”のような最早普通には使われなくなっ\nたものもあるが、いくつかはリストに登録すべき普通の単語もある。\n'}, {'id': '6f017cc5-f41e-4d50-8537-4d95bed4410d', 'page': 185, 'text': '13.7.乱雑な単語選択185練習問題13.6Pythonは集合の演算をサポートするsetと呼ばれるデータ構造を提供している。文献http://docs.python.org/lib/types-set.htmlを読み、本から抽出した単語で単語集にない単語を探すためにこのsetを使ったプログラムを書け。13.7乱雑な単語選択ヒストグラムに従って単語を乱雑に選択するためには、最も簡単な方法はその出現頻度に従って単語のコピーを作りリストの要素とし、そのリストに従い乱雑な要素を選択することであろう。つまり、defrandom_word(h):t=[]forword,freqinh.items():t.extend([word]*freq)returnrandom.choice(t)表式[word]*freqは文字列wordをfreq個集めたリスト生成する。メソッドextendはappendに似ているが引数が配列であることが違う。練習問題13.7上のアルゴリズムでも動く。しかし効率が悪いし、再構成されたリストは元の本程度の大きさになる。自明に近い改良は、リストはそのままで単語の選択を複数個同時選択させることだ。それにしてもリストは大きすぎだ。別な解法を示す:1.ヒストグラムのキーである単語をリストにする。2.単語の頻度の累積(練習問題10.3をみよ)を要素とするリストを作成する。したがってこの要素の最後の要素は全単語数、nになる。3.1からnまでの乱数を発生させる。二分探索法(練習問題10.11)を使ってこの乱数を内挿値とする累積リストのインデックスを得る。4.このインデックスに従って単語リストから単語を選択する。本から乱雑に単語を選択するこのアルゴリズムを使ったプログラムを作成せよ。解答例:http://thinkpython.com/code/analyze_book3.py\x0c'}, {'id': '8f1d08b9-e46b-4a31-bddb-0de18adb25e2', 'page': 186, 'text': '186第13章事例研究:データ構造・選択13.8マルコフ解析本から乱雑に単語を選択して、並べると単語の並びができるが、それは文章にはなっていないだろう。例えばこうだ:thisthesmallregardharrietwhichknightley’sitmostthings連続する単語間に何も関係がないので、このような単語の並びは滅多に文章を作ることはない。例えば、現実の文章では、定冠詞“the”の後には形容詞や名詞が続き、動詞や副詞がくることはない。現実の単語間の関係を測定する方法の一つにマルコフ解析がある。ある単語の並びに対して、ある単語がそれに続く確率として表現される。例えばEric,theHalfabee(訳注:歌詞の意味はhttp://en.wikipedia.org/wiki/Eric_the_Half-a-Beを参照のこと)の出だしはこうだ:Halfabee,philosophically,Must,ipsofacto,halfnotbe.ButhalfthebeehasgottobeVisavis,itsentity.D’yousee?ButcanabeebesaidtobeOrnottobeanentirebeeWhenhalfthebeeisnotabeeDuetosomeancientinjury?例えばこのテキストでは、句“halfthe”の後には“bee”が常に続くし、句“thebee”の後は、“is”か“has”かである。マルコフ解析の結果はプレフィックス(“halfthe”や“thebee”のような)とサフィックス(“has”や“is”のような)の間の写像として表現される。この写像が与えられると、まずあるプレフィックスから文章を始める。このプレフィックスに続くものは写像として許させるサフィックスの内から乱雑に一つ選ぶことで実現できる。次ぎには、このプレフィックスの最後の単語とサフィックスを繋いだものを新たなプレフィックスとして前と同じ手続きを繰り返す。例えば、プレフィックスとして“Halfa”で始めたとすると、サフィックスとしての候補は“bee”しかないので、この“bee”が続く。次のプレフィックスは“abee”である。これに従うサフィックスの候補は“philosophically”、“be”、“due”である。この例ではプレフィックスの長さは二単語であるが、任意の長さのマルコフ解析ができる。この長さを解析のオーダーと呼ぶ。練習問題13.8マルコフ解析1.ファイルからテキストを読み、マルコフ解析を行え。結果はプレフィックスをキーとして可能なサフィックスをまとめたものを値とする辞書である。このまとめたものはタプル、リスト、辞書の形式があるが、これはあなたの選\x0c'}, {'id': 'af981a78-10f4-4f39-8ac2-0130430734dc', 'page': 187, 'text': '13.9.データ構造187択に任せる。プログラムのテストではプレフィックスの長さは2でよいが、任意の長さに対応するようにせよ。2.マルコフ解析に従ってテキストを乱雑に生成するプログラムを作り、前のプログラムに追加せよ。以下はEmmaをオーダー2のマルコフ解析から得られた例である:Hewasveryclever,beitsweetnessorbeangry,ashamedoronlyamused,atsuchastroke.ShehadneverthoughtofHannahtillyouwerenevermeantforme?””Icannotmakespeeches,Emma:”hesooncutitallhimself.この例では単語に付いていた句読点はそのままその単語に付けて用いた。結果は英語構文的にはほぼ、正確には充分でないが、正しい。意味論的には、ほぼ意味が通るが、正確には充分でない。マルコフ解析のオーダーを高くしたらどうなるがろう?もっと意味論的に意味が通るテキストができるのだろうか?3.プログラムが動くようになったら、混合したらどうなるか確かめることにしたい。二つ以上の本をマルコフ解析して、それに従ってランダムテキストを生成してみるとこれは解析に用いた本の単語や語句をブレンドしたものになるはずだ。出典:KernighanandPike著“ThePracticeofProgramming”(Addison-Wesley,1999)の例に啓発されてこの事例研究を作成した。例によって、練習問題は答えをみる前に解答を試みること。解答例:http://thinkpython.com/code/markov.pyまた解析に使ったテキストとしてhttp://thinkpython.com/code/emma.txtが必要。13.9データ構造マルコフ解析によるランダムテキストの生成は面白いが、この事例研究はデータ構造の選択に関する問題も含んでいる。その選択では:•プレフィックスをどのように表現するか?•可能なサフィックスの集合をどのように表現するか?•各プレフィックスからサフィックスの集合へのマッピングをどう表現するか?\x0c'}, {'id': '07f08e86-edb4-45d2-98ab-6237e9e7128b', 'page': 188, 'text': '188第13章事例研究:データ構造・選択最後の問題が最も簡単に決められそうである。つまり、辞書を使うことだ。プレフィックスに関しては、文字列、文字列のリスト、文字列のタプルなどが候補になるだろう。サフィックスに関しては、リストまたはヒスとグラム(辞書型)が考えられる。どのような基準で選択するか?第一に考慮すべきは想定したデータ構造に対してしなければならない操作について検討することだ。新たなプレフィックスを生成するために、プレフィックスの先頭の単語を削除して、単語を一つ追加する操作が必要になる。例えば、今のプレフィックスは“Halfa”であって、次ぎの単語が“bee”であったとすると、新しいプレフィックスは“abee”となる(訳注:単語を一つずらして“Half”を消して“bee”を加える)。このように考えるとプレフィックスにはリストが便利なことが分かる。しかし、またプレフィックスは辞書のキーにもならなければならない。だとするとリストではダメである。タプルでは変更不可なので削除や追加はできないが、追加を関数で処理し、新しいタプルを生成することで代替できる:defshift(prefix,word):returnprefix(1:)+word関数shiftは単語のタプルprefixと文字列wordを引数として持ち、最初の単語を除外した単語のタプルと単語wordを追加した新しいタプルを返す。サフィックスの集合に関しては、新たな要素を追加する(またはヒストグラムであると頻度を1上げる)、及びランダムにサフィックスを選択するという操作がある。リストからランダムに選択することは容易だが、ヒストグラムでは少し難しい操作(練習問題13.7をみよ)になる。これまでは実装の容易さについてのみ考察したが、データ構造の選択は他の要素からも考察する必要がある。実行時間はその一つである。ある場合は理論的な推定からあるデータ構造が他のものより速いということが分かることがある。例えば、リストと比較して辞書にしておいた方が操作は速いことに既に言及した。しかし、多くの場合は前もってこのような実行時間の推定はできない。そのようなときに取る得る選択肢は双方のデータ構造を使って実行時間を実測してみることだ。これはベンチマーク・テスト(benchmarking)と呼ばれている方法だ。実際的には、実装することが最も容易なデータ構造を選択し、速度の点から実用に耐え得るかをみるのがよいだろう。もしよければこれ以上は必要ない。もしもそうでなければ、profileのようなツールを使ってプログラムで時間を食っている個所を特定してみるとよい。考察の次ぎは記憶領域の消費の程度だ。例えば、サフィックスの集合をヒストグラムにすると保存すべきある単語がいかに多数回テキストに現れても一回だけの保存で済むのでリストより少ない記憶領域で処理できる。ある場合には少ない記憶領域で済むことは実行速度を速めることにもなる。メモリーを食い尽くしてし\x0c'}, {'id': '26912c8d-eb4f-4806-9ae9-79f4b406f486', 'page': 189, 'text': '13.10.デバッギング189まったら動かなくなることもある。しかし、一般のアプリケーションでは記憶領域の大きさは速度に比較したら第二義的な問題だ。最後にもう一つの考察をしよう。解析時とテキスト生成時のデータ構造は同じとしてきたが、これは二つが分離できることから、それぞれ異なったデータ構造を使う選択肢もある。一つのデータ構造を解析に用いて、それを必要に応じて変換したデータ構造でテキスト生成を行うわけである。テキスト生成時に於ける実行時間の節約が変換に必要な時間を超えていれば全体の時間は節約できる。13.10デバッギングプログラムをデバッグしていて、特に見つけにくいバグに遭遇したときには以下の四つのことをしてみよう:読め:自分が書いたコードを吟味せよ。自分に言い聞かせるように読め。そして自分の言いたいことが書けているか調べよ。実行せよ:プログラムを変更し、異なったバージョンを実行する実験をせよ。ときとして、しかるべきところで表示をしてみることで、問題が自明になるということもある。しかし、このような足場を幾重にも組まなければならないときもある。熟考せよ:時間をかけて考えよ。それはどのようなエラーか、構文なのか、実行時なのか、意味的なエラーなのか?エラーメッセージまたはプログラムの表示から得られた情報は何なのか?どんなエラーが懸案の問題を引き起こすだろうか?問題が最初に現れたのは何をしたときか?後退せよ:いくつかのタイミングで、最善の策は進行している変更を破棄し、プログラムがそれなりに動き、理解ができる時点のプログラムまで戻ることだ。そして、この状態からプログラムを再構築すればよい。初心者はとかくこれらのやらなければならないことの一つのみに執着しがちだ。そして、他の可能性を忘れてしまう。これらの四つの行動は起因するエラーの種類に対応したものだ。例えば、コードを詳しく読むことはコードが含むタイプミスを見つけるには役に立つが、問題が概念的な誤解からくるものであると役に立たない。自分がプログラムとして作っていることがらを理解していないならば、コードを百回読んでもエラーを見つけることはできない。エラーは自分の頭の中にあるのだから。\x0c'}, {'id': 'e8c9198e-978c-4a81-99fa-bb6cfc653a66', 'page': 190, 'text': '190第13章事例研究:データ構造・選択特に小規模で簡単なテストでプログラムを実行してエラーを探すことは有益である、しかし、考えもしないでコードを読まずに単に実行を繰り返すことは、私が「酔歩プログラミング」と呼ぶ欠陥に陥る。これはプログラムが正常に動くまで、適当に場当たり的に修正を繰り返すことになる。この酔歩プログラミングはデバッグに多大な時間がかかることは言うまでもない。考えることに時間を割くべきだ。デバッギングは実験科学のようなものだ。問題の所在について少なくとも一つの仮説を立てるべきだ。二つも三つもあるときは一つずつ潰していけばよい。休憩は考察のために役に立つ、他の人に話をする機会もそうだ。問題を他の人、または自分自身でもよい、に説明しようとして、話が終わる前に答えが分かってしまうことがあるものだ。エラーが大量で、小さいテストを実行するにはコードが膨大で複雑であるとこれら最善のデバッギングの技術を用いても失敗することがある。このような状況では最善の選択肢は後退である場合がある。自分が理解でき、何かの手懸かりが掴まえられるまでプログラムを単純にしてみることだ。せっかく書きあげたコードであるので、それが間違いを含んでいてもそのコードの一行も削除することに我慢ができないので、初心者は後退に躊躇しがちである。コピーで気持が納まるのであれば、コピーを作り、コードを推敲すればよい。コピーされたものから少しずつ推敲中のコードに追加するようにする。見つけにくいバグに遭遇したときは、読み、実行し、熟考し、ときとして後退することが必要だ。もし自分がこれらのやらなければならないことの一つのみに執着しているようだったら、他も試してみよう。13.11語句決定論的(deterministic):同一の入力を与えられて実行されたプログラムが各時刻に同一の出力を出すといったプログラムの関係性。疑似乱数(pseudorandomnumber):見かけの上では乱雑に見えるが決定論的な過程で生成された数の列が持つ性質。既定値(defaultvalue):引数が与えられない場合に使う前もって準備された最適な値。無効にする(overrides):既定値を引数で置き換える。ベンチマーク・テスト(benchmarking):データ構造の選択にあたって幾つかの候補になるデータ構造を実装し同じ実際のデータを与えて実行して優劣を決める過程。\x0c'}, {'id': 'b98083fb-1d5d-4006-86f7-cca6aeebf0dd', 'page': 191, 'text': '13.12.練習問題19113.12練習問題練習問題13.9単語出現頻度に従って単語を並べてときの単語の順位をその単語の「ランク」という。最頻度単語のランクは一位で、その次ぎ二位である。自然言語に対して、ジップの法則は単語の頻度とそのランクの間にある関係を述べてものである(http://en.wikipedia.org/wiki/zipf’s_law)。それによれば、単語の頻度fはその単語のランクrから以下の関係で予測される:f=cr−sここでsとcは使用言語とテキストによって決まる定数である。両辺の対数をとるとlogf=logc−slogrとなる。従って、両対数グラフを作ると傾きが-sで接片がlogcの直線が得られる。テキストから英文を読み込み、単語出現頻度を調べよ。そして、頻度の降順に一行毎に各単語のlogf、logrを表示せよ。手元にあるグラフ表示ソフトウエアを使って結果をグラフ化し、直線が現れるか調べよ。傾きsの値は推定できるか?解答例:http://thinkpython.com/code/zipf.py.グラフ表示にはmatplotlibが利用できる(http://matplotlib.sourceforge.net)。\x0c'}, {'id': '242cb617-f1f8-42b3-a09c-66cf592bc5ae', 'page': 1, 'text': ''}, {'id': '670e3bad-84b7-4d88-bfe9-291a7287fb53', 'page': 193, 'text': '193第14章ファイル14.1永続性これまでみてきたプログラムは短時間実行し、結果を表示し、それが終われば消えてしまうという意味で過度的なものだ。もしプログラムを再度走らせようと思ったら、まっさらな状態からに再実行することしかない。他のプログラムは永続的(persistent)だ。それらは長い時間(または常に)動いている:それらはデータの一部を外部記憶装置(例えばハードディスク)に保存しておく。そして、プログラムが終了しても、終了時のデータを使って再実行ができる。このような永続的なプログラムの典型はオペレーティング・システムで、コンピュータが起動している間はほぼ動いている。WEBサーバはもう一つの例だ。これはネットワーク上の要求に対応するように常に動いている。プログラムが扱うデータを保持する最も簡単な方法はテキストファイル(textfile)の読み込み書き込みである。テキストファイルの読み込みについては既に触れたので、この章では書き込みを議論する。また別な方法はプログラムの状態をデータベースに保存しておくものだ。この章では簡単なデータベースを示し、プログラムの状態をこのデータデースに保存するときに使うモジュールpickleを紹介する。14.2読み込み・書き込みテキストファイルは外部記憶装置(ハードディスク、フラッシュ・メモリー、CD-ROM等)に文字ベースで保存される。既に、9.1節で読み込みのためのopenとreadについては学習した。書き込みは’w’モードでファイルをオープンする:>>>fout=open(’output.txt’,’w’)>>>printfout\x0c'}, {'id': 'a4df2de8-f4db-49f1-80b0-fd91a8db2e68', 'page': 194, 'text': '194第14章ファイル既存のファイルであると古いデータを全て消去するので要注意。存在しないものであると新規にフィアルを作成する。メソッドwriteはデータをフィアルに書き込む:>>>line1="Thishere’sthewattle.\\n">>>fout.write(line1)ファイルオブジェクトは現在ファイルの何処にいるのかの追尾機能を持っているので、writeでもう一度書き込むとデータはそれまでの終わりに追加されることになる:>>>line2="theemblemofourland.\\n">>>fout.write(line2)14.3記述演算子メソッドwriteの引数は文字列でなければならない。従って他の値をファイルに書き込むにはそれらを文字列に変換する必要がある。その最も安易な方法はstrを用いるものだ:>>>x=32>>>f.write(str(x))他の方法として、記述演算子(formatoperator)%を使うものがある。整数に適用した%はモジュラー演算子であるが、最初の被演算子が文字列であるときには記号%は記述演算子となる。最初の被演算子は記述文字列(formatstring)で、第二の被演算子を如何に記述するかを指定する少なくとも一つの記述子(formatsequence)を含んでいる必要がある。整数を記述する’%d’記述子(dは“digital”の略語である)を例にみよう。>>>camels=42>>>’%d’%camels’42’結果は文字列の’42’である。整数の42と混乱しないように。記述子は文字列の任意の場所に置くことができ、値を文章の任意の場所に挿入できる:\x0c'}, {'id': '7e569d24-a085-4c97-af5e-e40327b08374', 'page': 195, 'text': '14.4.ファイル名とパス195>>>camels=42>>>’Ihavespotted%dcamels,’%camels’Ihavespotted42camels,’二つ以上の記述子が文字列中にあるときは、第二被演算子はタプルでなければならない。以下の例では’%d’は整数のため、’%g’は浮動小数点数のため、’%s’は文字列のために使われている:>>>’In%dyearsIhavespotted%g%s.’%(3,0.1,’camels’)’In3yearsIhavespotted0.1camels.’タプルの要素の個数は記述子の個数と一致している必要があるし、要素の型は記述文字列の中の型と合っている必要がある。>>>’%d%d%d’%(1,2)TypeError:notenoughargumentsforformatstring>>>’%d’%’doliara’TypeError:%dformat:anumberisrequired,notstr最初の例では個数が一致していないし、第二の例では型が一致していない。記述演算子は強力であるが使い方が難しい。より詳細はdocs.python.org/lib/typesseq-strings.htmlを参照のこと。14.4ファイル名とパスファイルはディレクトリー(directories)を使って組織化されている。実行中のプログラムの全てが大部分の操作に対して既定値となる「カレント・ディレクトリー」と呼ばれるディレクトリーを持っている。例えば、プログラムでファイル読み込みのためにopen文を実行すると、Pythonはこのカレント・ディレクトリーにそのファイルを探しに行く。モジュールosはファイルやディレクトリーに関連する操作をサポートする関数を提供している(osは“operatingsystem”の略である)。os.getcwdはカレント・ディレクトリーの名前を返す:>>>importos>>>cwd=os.getcwd()>>>printcwd/home/dinsdale\x0c'}, {'id': 'c90320b6-aba3-4956-862d-21c131799366', 'page': 196, 'text': '196第14章ファイルcwdは“currentworkingdirectory”の略である。この例の結果は/home/dinsdaleでユーザdansdaleのホームディレクトリーである。cwdのようなファイルの所在を示す文字列はパス(path)と呼ばれる。相対パス(relativepath)はカレント・ディレクトリーを起点とするパス表示で、絶対パス(absolutepath)はファイルシステムのトップディレクトリーを起点としたパス表示である。今までみてきたパスはファイル名のみだったので、これは相対パスを示している。絶対パスを表示するにはos.path.abspathを使う:>>>os.path.abspath(’memo.txt’)’/home/dinsdale/memo.txt’os.path.existsはファイルやディレクトリーが存在するかどうかを調べる(訳注:探索パス上に存在すかどうかである)。>>>os.path.exists(’memo.txt’)Trueもし存在することが判明したら、os.path.isdirでそれがディレクトリーであるかどうかを調べることができる:>>>os.path.isdir(’memo.txt’)False>>>os.path.isdir(’music’)True同様に、os.path.isfileでそれがファイルであるかどうかを調べることができる。os.path.listdirで与えられたディレクトリー内のファイル名や他のディレクトリー名のリストを得ることができる。>>>os.path.listdir(cwd)[’musics’,’photos’,’memo.txt’]これらの関数の機能を以下のプログラムで示す。これは一つのディレクトリー内を「逍遙」して全てのファイル名を表示し、その中の全てのディレクトリーを再帰的に探索する。defwalk(dirname):fornameinos.listdir(dirname):path=os.path.join(dirname,name)\x0c'}, {'id': 'a4624bd5-7867-4924-9e91-37ce499514d4', 'page': 197, 'text': '14.5.例外捕捉197ifos.path.isfile(path):printpathelse:walk(path)os.path.joinはディレクトリー名とファイル名を引数に取り、完全なパス名を返す。練習問題14.1モジュールosはここで提示した関数walkがある。この関数のドキュメンテーションを読み、あるディレクトリー内のファイル名、その中にあるディレクトリー内のファイル名というように全てのファイル名を表示するプログラムを作成せよ。解答例:http://thinkpython.com/code/walk.py14.5例外捕捉ファイルの読み書きでは沢山の事柄が上手く行かないことがある。存在しないファイルを開こうとするとIOErrorが出る:>>>fin=open(’bad_file’)IOError:[Errno2]Nosuchfileordirectory:’bad_file’また、許可のないファイルにアクセスしようとすると同様にエラーになる:>>>fout=open(’/etc/passwd’,’w’)IOError:[Errno13]Permissiondenied:’/etc/passwd’更に、ディレクトリーを書き込みモードで開とエラーになる:>>>fin=open(’/home’)IOError:[Errno21]Isadirectoryこれらのエラーを避けるためには、os.oath.existsやos.path.isfileのような関数を使うことも考えられるが、可能性のある全てのエラーを調べるには時間もかかるし、コードも余計に太る。それよりもさし当たって問題が惹起されるか試みて、問題があれば対処するという方法がよりよい。これはtry文が意図するものである。構文はif文に似た書き方をする:\x0c'}, {'id': '2719c495-88ab-4972-bfdf-046082a0de5b', 'page': 198, 'text': '198第14章ファイルtry:fin=open(’bad_file’)forlineinfin:printlinefin.closeexcept:print’Somethingwentwrong.’Pythonはtry句を先ず実行する。何も問題がなければ、except句は無視し次ぎに進む。もし何か例外が起こるとtry句を中断してexcept句を実行する。try文による例外の処理は例外捕捉(catching)と呼ばれている。上の例ではexcept句は単にエラーが起きたことを知らせる表示だけでありあまり役に立たないが、一般にexcept句は問題の解決法、再実行、少なくとも優雅にプログラムを終了させる位のことはする。練習問題14.2探索文字パターン、置換文字パターンの二つの文字列を引数に、更に二つのファイル名を引数とする関数sedを作成せよ。一つのファイルは読み込み用のテキストファイルで、他は書き込み用のファイルである。関数はテキストを読み込み、その中に探索文字パターンがあるときは、この文字列を置換パターンに置き換えてテキストを書き込みファイルに書き出す。ファイルのオープン、読み込み、書き込み、クローズに際してエラーがある場合には、例外捕捉でエラー表示をし、プログラムを終了するようにせよ。解答例:http://thinkpython.com/code/sed.py14.6データベースデータベース(database)はデータ保存のために組織化されたファイルである。大部分のデータベースはキーから値への写像という特徴をもつので辞書のように組織かされている。最大の違いは、データベースはハードディスクのような外部記憶装置上に作られ、従って、プログラムが終了しても永続的に残ることである。モジュールanydbmはデータベース・ファイルの作成及び更新の操作を提供している。例としてここでは画像ファイルの脚注を保存するデータベースを作成する。データベースのオープンはファイルのそれと似ている:>>>importanydbm>>>db=anydbm.open(’captions.db’,’c’)\x0c'}, {'id': '0f00a02b-a788-4b26-8eec-d78f785cb131', 'page': 199, 'text': '14.7.削ぎ落とし199モードcはもしデータベースが存在しないときは作成するという意味である。結果は辞書が持つ操作の大部分を提供するデータベースオブジェクトを戻す。新しいアイテムを追加しようとすると、anydbmはデータベースを更新する。>>>db[’cleese.png’]=’PhotoofJohnCleese.’アイテムにアクセスしようとすると、anydbmはファイルからの読み込みをする:>>>printdb[’cleese.png’]PhotoofJohnCleese.もし既存のキーに代入を試みると、古い値の上書きになる:>>>db[’cleese.png’]=’PhotoofJohnCleesedoingasillywalk.’>>>printdb[’cleese.png’]PhotoofJohnCleesedoingasillywalk.辞書で使えたメソッド、例えばkeysやitemsなども使える。またfor文での繰り返しもできる:forkeyindb:printkey終了は、普通のファイルのようにクローズで終わる:>>>db.close()14.7削ぎ落としanydbmの制限はキーも値も文字列のみが許されることだ。他の型ではエラーになる。モジュールpickleはこんなときに役に立つ。このモジュールはほぼ任意の型のデータを文字列に変換し、また逆に型データに逆変換する。pickle.dumpsは引数として任意の型を受け取り、対応する文字列による表現を返す(dumpsは“dumpstring”の略)。>>>importpickle>>>t=[1,2,3]>>>pickle.dumps(t)’(lp0\\nI1\\naI2\\naI3\\na.’\x0c'}, {'id': '1a94e38f-35aa-48ea-9846-f58737eac544', 'page': 200, 'text': '200\n第14 章\nファイル\nこの文字列の記述法はヒトには不明だ。これはpickle にとって容易に解釈できる\nことを意味している。事実pickle.loads(つまり“load string”)はオブジェクト\nを復元する。\n>>> t1 = [1, 2, 3]\n>>> s = pickle.dumps(t1)\n>>> t2 = pickle.loads(s)\n>>> print t2\n[1, 2, 3]\nこの新しいオブジェクトは古いものと同じ値を持っているが、一般に同一のオブ\nジェクトではない:\n>>> t1 == t2\nTrue\n>>> t1 is t2\nFalse\nつまり、\n「削ぎ落とし」\nと\n「その復元」\nはオブジェクトをコピーしたことに対応する。\nこのpickle モジュールを使って文字列でない型のデータをデータベースで扱う\nことができる。モジュールshelve はこれらの機能をカプセル化したものである。\n練習問題14.3 練習問題12.4 の解答\nhttp://thinkpython.com/code/anagram_sets.py をダウンロードしてコードを\n眺めてみると単語をキーにしてこの単語から派生するアナグラムにある単語のリス\nトを値とする辞書を使っていることがわかる。\n例えば、\n’opst’のアナグラムにある単\n語のリストは(’opts’,’post’,’pots’,’spot’,’stop’,’tops’)となる。このanagram sets\nをインポートし、二つの新しい関数を作成せよ。一つはアナグラム辞書を「棚」に\n保管するstore anagrams、もう一つはこのデータベースからキーに対応するアナ\nグラムのリストを呼び出す関数read anagrams である。\n解答例:http://thinkpython.com/code/anagram_db.py\n14.8\nパイプ\n多くのオペレーティング・システムはシェル(shell)として知られているコマ\nンドベースのインタフェースを持っている。シェルはファイルシステムを探索し\nたり、アプリケーションを起動させたりするコマンドの体系を提供している。例\n'}, {'id': 'faafd776-a26b-4c89-aeb8-3b5407994921', 'page': 201, 'text': '14.8.\nパイプ\n201\nえば、Unix ではディレクトリーの変更はcd、ディレクトリーの中味の表示はls、\nまたweb ブラウザーの起動は(例えば)firefox のコマンドで行う。\nシェルから起動できるプログラムはパイプ(pipe)を使ってPython からも起動\nできる。パイプは起動されているプログラムを表現しているオブジェクトである。\n例えば、Unix コマンドls -ll はカレント・ディレクトリーの中味を表示するも\nのであるが、os.popen 1 でこのコマンドを実行できる:\n>>> cmd = ’ls -l’\n>>> fp = os.popen(cmd)\n引数はコマンドを表現する文字列である。戻り値はファイルのオープン文で戻る\nようなオブジェクトである。メソッドreadline でls プロセスの表示を一行毎に表\n示できるし、read で一度に全てを表示できる。\n>>>res = fp.read()\n終わるときはファイル同じようにクローズする:\n>>> stat = fp.close()\n>>> print stat\nNone\n戻り値はls プロセスの終了状態である。None はそのプロセスが正常に終了した\nことを示す。\n他の例である。多くのUnix システムはファイルの中味を読み、“checksum”とい\nう量を計算するコマンドmd5sum を提供している。\nこのMD5 についてはhttp://en.wikipedia.org/wiki/Md5 を参照せよ。このコマ\nンドは二つのファイルの中味が同一なものであるかどうかを調べる上で有効な方\n法を提供している。中味が異なるファイルが同じchecksum を生成する確率は極め\nて小さい(宇宙が崩壊する以前には起こりそうもない)\n。Python からパイプを使っ\nてこのmd5sum を起動することができ、結果も得られる。\n>>> filename = ’book.tex’\n>>> cmd = ’md5sum ’ + filename\n>>> fp = os.popen(cmd)\n>>> res = fp.read()\n>>> stat = fp.close()\n1popen は旧式であると言われている。その意味はこれに替わる新しいモジュールsubprocess\nを使うことが期待されている。しかし、簡単なケースではsubprocess は必要以上に複雑になって\nしまう。わたしはそれが解決されるまではpopen を使うつもりである。\n'}, {'id': '09aafa9d-cc89-49ab-bcca-1d4b8edcf5d7', 'page': 202, 'text': '202第14章ファイル>>>printres514f67fee821e1d0f353051c2094de78book.tex>>>printstatNone練習問題14.4MP3ファイルの膨大なコレクションがある。多分同じ内容の曲が異なった名前や異なったディレクトリーにあることが少なからずあると思われる。この練習問題はこの重複を探索する方法である。1.あるディレクトリー内及びそのサブディレクトリーと再帰的に調べ特定の拡張子(.mp3のような)を持つ全てのファイル対する完全パスを要素とするリストを生成するプログラムを作成せよ。2.重複を確認するために、各ファイルの“checksum”を計算するためにmd5sunを利用する。二つのファイルが同一の“checksum”だったら、この二つのファイルは中味が同じとみてよい。3.二重のチェックとしてUnixコマンドのdiffを使うこともできる。解答例:http://thinkpython.com/code/find_duplicates.py14.9モジュールを書く任意のPythonコードを含むファイルはモジュールとしてインポートできる。例として、以下のようなコードを含むwc.pyを考える:deflinecount(filename):count=0forlineinopen(filename):count+=1returncountprintlinecount(’wc.py’)このプログラムを起動するとファイルの行数、つまり7を表示するはずだ。このファイルをインポートもできる:>>>importwc7\x0c'}, {'id': 'e1f3d009-db55-4496-a763-f6d4089dd534', 'page': 203, 'text': '14.10.デバッギング203ここではwcはモジュールオブジェクトになっている:¿¿¿printwc¡module’wc’from’wc.py’¿このモジュールはlinecountという関数を提供することになる:>>>wc.linecount(’wc.py’)7これでPythonのモジュールを書いたことになる。唯一の問題はこの例ではモジュールをインポートした時点で、コードの最後に書いたコードのテストが実行されてしまうことである。通常はモジュールのインポートは関数類の定義でありその実行までは必要ない。モジュールとして使う予定のプログラムはよく以下のような常套句を使う:if__name__==’__main__’:printlinecount(’wc.py’)__name__は組み込み変数でプログラムが起動されたときに値が決まる。もしもプログラムがスクリプトとして起動された時はこの値は__main__である。その場合はテストコードが実行される。さもないと、つまりモジュールとしてインポートされるとテストコードはスキップされる。練習問題14.5例題のwc.pyをファイルとして作成せよ。そしてPythonを起動し、wcをインポートしてみる。この時点で__name__は如何なる値を持っているか?注:もしもインポートしようとしたモジュールが既にインポートされているとすると、そのモジュールが変更を受けていようがPythonは何もしない。再度モジュールをインポートしたいときには、組み込み関数reloadが使える。しかし、扱いにくいので、最も安全な方法はインタプリタを再起動し、再度モジュールをインポートすることだ。14.10デバッギングファイルの読み書きでデータ区切り文字の問題に遭遇するかもしれない。普通空白、タブ、改行は見えないので、この種のエラーはデバックが難しい:>>>a=’1,2\\t3\\n4’>>>printa1,234\x0c'}, {'id': 'a7056f99-3b67-4028-a8e9-b94908602d27', 'page': 204, 'text': '204第14章ファイル組み込み関数reprがこのときに役に立つ。この関数は引数として任意のオブジェクトをとり、そのオブジェクトを表現する文字列を返す。文字列であるとデータ区切り文字を含めて表示される:>>>printrepr(a)’1,2\\t3\\n4’他の問題としては行の終わりを示す文字が異種システム間で違っていることだろう。あるシステムでは行の終わりは\\nになるが、他のシステムでは\\rであり、またこの両方で行の終わりを示すシステムもある。異なったシステム間でファイルのやり取りをするときに問題になる可能性がある。多くのシステムでは変換のためのアプリケーションがある。それらを見つけてみよう。更にhttp://en.wikipedia.org/wiki/Newlineも参照のこと。勿論、あなた自身でそのプログラムを書くのもよし。14.11語句永続的(persistent):休みなく実行され少なくともそのデータの一部が外部不揮発記憶装置に保存されようなプログラムの性格。記述演算子(formatoperator):記述文字列と記述子(タプルになっている)を受け取り、記述子の各要素を記述文字列に従って文字列に変換することを含めた文字列を生成する演算子%である。記述文字列(formatstring):記述演算子と共に使われる記述子を含む文字列。記述子(formatsequence):記述文字列の中で値を如何に文字列に変換するかを指定する%dのような文字列。テキストファイル(textfile):ハードディスクのような外部装置に保存される文字だけのデータ。ディレクトリー(directories):固有の名前が付けられたファイルの集合。フォルダーとも言う。パス(path):一つのファイルを同定するための文字列。相対パス(relativepath):カレント・ディレクトリーから辿ったパス。絶対パス(absolutepath):ファイルシステムの最上位のディレクトリーから辿ったパス。.\x0c'}, {'id': '52327c4b-be04-4359-96b9-6cdcc40d2f83', 'page': 205, 'text': '14.12.練習問題205例外捕捉(catching):try文やexcept文を用いてプログラムの異常終了を回避する手法。データベース(database):その内容がキーと対応する値を辞書で組織化した中身になっているファイル。14.12練習問題練習問題14.5モジュールurllibはWeb上のURLを操作するメソッドを提供している。以下の例はthinkpython.comから秘密のメッセージをダウンロードし、表示するものである。importurllibconn=urllib.urlopen(’http://thinkpython.com/secret.html’)forlineinconn:printline.strip()上記のコードを実行してそこに書かれている命令に従え。解答例:\u3000http://thinkpython.com/code/zip_code.py\x0c'}, {'id': '39c8af6a-6dd4-4212-a234-40f1196e24d5', 'page': 1, 'text': ''}, {'id': '77a741c4-7616-4a61-9ff5-e8a431222b2c', 'page': 207, 'text': '207第15章クラスとオブジェクトこの章の例題のコードはhttp://thinkpython.com/code/Point1.pyで入手可能である。さらに例題の解答例はhttp://thinkpython.com/code/Point1_soln.pyにある。15.1ユーザ定義型これまで組み込み型についてみてきたが、この章では新しい型を定義してみよう。例として二次元平面上の点を表現するPointと呼ばれる型を生成してみよう。数学的な表現では二次元上の点は二つ座標の値をカンマで区切り、カッコで囲む。例えば、(0,0)は原点を示し、(x,y)は右にx単位移動し、上にy単位だけ移動した点の表現になる。Pythonでこの状況を表現する方法はいくつもある:•変数xと変数yを用意し、座標の値を保存する。•リストやタプルを用意し、その要素として二つの値を保存する。•座標点の表現するような新しい型を創造してその型の変数に保存する。新しい型を創造するのはちょっと複雑のように思われるが直ぐみるように利点も大きい。ユーザ定義型はクラス(class)とも呼ばれている。クラスの定義は以下のようなものだ。classPoint(object):"""Representsapointin2-Dspace."""ヘッダーはこの新しいクラスの名前がPointであることを示している。そして、このクラスが組み込み型objectの一種であることを宣言している。ボディはこのクラスの目的を記述した文書である。ここには変数の宣言やメソッドを記述することができるが、それは後の話題とする。\x0c'}, {'id': '185f1c34-8ab7-42ea-86ff-cc37c6e8c499', 'page': 208, 'text': '208第15章クラスとオブジェクトクラスを宣言することで新たなクラスオブジェクト(classobject)を生成したことにもなる。>>>printPointこのクラスはプログラムのトップで宣言されたのでその完全名は__main__.Pointになる。クラスオブジェクトはオブジェクトを生成する工場のようなものである。一つのPoint型の変数を生成するには、関数を呼ぶようにクラスを使う:>>>blank=Point()>>>printblank<__main__.Pointobjectat0x011FCF50>戻り値はPointオブジェクトへの参照であり、これが変数blankに代入される。新しいオブジェクトを生成することをオブジェクト具現化(instantiation)と呼び、生成されたオブジェクトをクラスのインスタンス(instance)という。インスタンスをprintすると、それがどのクラスに属しているか、メモリー上のどこに保存されたのか(0xのプレフィックスが付いていることから十六進数)を表示する。15.2属性以下のようにインスタンスに値を与えることができる:>>>blank.x=3.0>>>blank.y=4.0ここでの構文はモジュールから変数を取り出す、math.piやstring.whitespaceのようなとき使う構文に似ている。しかし、ここではオブジェクトの名前付きの要素へ値を代入することである。この要素のことを属性(attributes)という。この英単語は名詞としては第一音節のアクセントがあるので、“AT-trib-ute”と発音するが、動詞としては、第二音節にアクセントがあるので、“at-TRIB-ute”と発音させる。図15.1はこの代入の結果を表すものである。一つのオブジェクトとその属性の状態を示すものでオブジェクト図という。変数blankはPointオブジェクトを参照し、二つの属性を持っていることを示す。各属性は浮動小数点数を参照している。属性の値は普通のように扱える:\x0c'}, {'id': 'ecf600ba-8dd8-4627-a138-58630bc8b8a6', 'page': 209, 'text': "# 15.2 属性\n\n## 図 15.1: オブジェクト図\n\n```\n>>> print(blank.y)\n4.0\n>>> x = blank.x\n>>> print(x)\n3.0\n```\n\n`blank.x`の意味は「`blank`が参照しているオブジェクトに行き、`x`の値を得よ」である。要するにこの場合はその値を変数に代入している。変数と属性の間には何も対立はない。ドット表記は任意の表示のなかで使える。例を示す:\n\n```\n>>> print('(%g, %g)' % (blank.x, blank.y))\n(3, 4)\n>>> distance = math.sqrt(blank.x ** 2 + blank.y ** 2)\n>>> print(distance)\n5.0\n```\n\nインスタンスで関数を呼び出すこともできる:\n\n```python\ndef print_point(p):\n print('(%g, %g)' % (p.x, p.y))\n```\n\n関数`print_point`は引数として`point`オブジェクトを受け取り、それを数学的な表記で表示する。これを発動させるときは引数にインスタンス`blank`を選けばよい:\n\n```\n>>> print_point(blank)\n(3, 4)\n```\n\nこの関数の中では`p`は`blank`の別名であるので、関数の中で`p`を変更すると`blank`も変わる。\n\n練習問題15.1二つの`point`オブジェクトを引数に取り、二点間の距離を戻り値とする関数`distance_between_points`を作成せよ。"}, {'id': '3c338247-9c5a-49ee-a95d-b41ab8d70827', 'page': 210, 'text': '210第15章クラスとオブジェクト15.3長方形オブジェクトの属性が明白であるものもあるし、そうでなく上手く決めなければならない場合もある。例として、長方形を表現するクラスを設計することを考えよう。その位置と大きさを指定するためにどのような属性が必要だろうか?簡単にするために長方形は垂直や水平に置かれたものとする。指定の仕方に少なくとも二つの方法があるだろう:•長方形の一頂点(または中心)の座標、幅、高さを指定する。•対角線上にある二つの頂点の座標を指定する。どちらがよいか決めかねるので、第一の方法を使う。クラス定義は以下のようだ:classRectangle(object):"""Representsarectangleattributes:width,height,corner"""ドキュメント文字列にある属性width,heightは数、cornerはPointオブジェクトである。一つの長方形を表現することはRectangleオブジェクトをオブジェクト具現化し、その属性に値を代入することである:box=Rectangle()box.width=100.0box.height=200.0box.corner=Point()box.corner.x=0.0box.corner.y=0.0表式box.corner.xの意味は「boxが参照するオブジェクトへ行き、cornerと名付けられた属性を選択し、そのオブジェクトへ行き、xと名付けられた属性を選択せよ」である。図15.2はこのオブジェクトのオブジェクト状態図(objectdiagram)を示している。他のオブジェクトの属性になっているオブジェクトは埋め込まれたオブジェクト(embedded\u3000object)である。\x0c'}, {'id': '14fc0361-49c9-47b6-8827-80d0dfa85987', 'page': 211, 'text': '# 15.4 戻り値としてのインスタンス\n\n関数の戻り値にインスタンスがくることがある。例を示す。関数 `find_center` は引数に `Rectangle` オブジェクトを取り、戻り値に `Rectangle` の中心の座標を含む `Point` オブジェクトを返す。\n\n```python\ndef find_center(rect):\n p = Point()\n p.x = rect.corner.x + rect.width / 2.0\n p.y = rect.corner.y + rect.height / 2.0\n return p\n```\n\n例えばインスタンス `box` を引数にしてみると、その中心の座標が戻ってくる:\n\n```python\n>>> center = find_center(box)\n>>> print_point(center)\n(50, 100)\n```\n\n# 15.5 オブジェクトは変更可能\n\nオブジェクトの属性の一つに値を代入するとオブジェクトの状態を変えることができる。例えば、一つの長方形の属性は変えずに、`width` と `height` を変えたい場合は以下のようにする:\n\n```python\nbox.width = box.width + 50\nbox.height = box.height + 100\n```\n\nオブジェクトを変更する関数を書くこともできる。例えば、`Rectangle` オブジェクトに二つの数、`width` と `height` を引数にはする関数 `grow_rectangle` はこの二つの数値分だけ長方形を大きくする関数である:'}, {'id': '007aa9a2-10af-4adb-8b7f-731670538306', 'page': 212, 'text': '212\n第15 章\nクラスとオブジェクト\ndef grow_rectangle(rect, dwidth, dheight):\nrect.width += dwidth\nrect.height += dheight\n以下はこの関数の効果を示す例である:\n>>> print box.width\n100\n>>> print box.height\n200\n>>> grow_rectangle(box, 50, 100)\n>>> print box.width\n150\n>>> print box.height\n300\n関数の中ではrect はbox の別名である。従って関数内でrect を変更すれば、box\nも変更される。\n練習問題15.2 引数としてオブジェクトRectangle と二つ数dx、dy を取り、長方\n形の場所の座標x とy をそれぞれdx とdy だけ移動する関数move rectangle を作\n成せよ。\n15.6\nコピー\n別名呼称は一ヶ所を変えると他に思わぬ効果が出るためプログラムを読み難く\nしてしまう。その結果あるオブジェクトを参照する全ての変数の軌跡を追跡する\nことを難しくしてしまう。\n別名呼称に替わるものとしてコピーがある。モジュールcopy は任意オブジェク\nトのコピーを生成する関数copy を提供している:\n>>> p1 = Point()\n>>> p1.x = 3.0\n>>> p1.y = 4.0\n>>> import copy\n>>> p2 = copy.copy(p1)\np1 とp2 は同じデータを含んでいるが、同一のPoint オブジェクトではない。\n'}, {'id': 'e4e4a013-b6cb-4efc-bb29-c31e069f8338', 'page': 213, 'text': '# 15.6 コピー\n\n```python\n>>> print_point(p1)\n(3, 4)\n>>> print_point(p2)\n(3, 4)\n>>> p1 is p2\nFalse\n>>> p1 == p2\nTrue\n```\n\nis選択子は二つが同一のオブジェクトでないことを意味し、これは予想したことだ。しかし、二選択子では結果はTrueと出ることを期待したかもしれないが、それを予想しなかった。インスタンスについては既定信じて、一選択子は選択子と同じ属性を持つ。さらに、この選択子は変更されるが、これは後の説明だ。`copy`関数を使うと`Rectangle`の複製を作ることができるが、埋め込まれたオブジェクト`Point`は違う。\n\n```python\n>>> box2 = copy.copy(box)\n>>> box2 is box\nFalse\n>>> box2.corner is box.corner\nTrue\n```\n\n図15.3はオブジェクトの状態図がどうなるかを示した。この操作はオブジェクトとそれが持ち得るものであるものは作成するが埋め込まれたオブジェクトはそのままであるので浅いコピー(shallow copy)と呼ばれる。大部分のアプリケーショ\n\n```\nbox = Rectangle(width=100.0,\n height=50.0,\n corner=Point(0.0, 0.0))\n\nbox2 = copy.copy(box)\n```\n\nジョンではこの事象はあって困くないものである。いくつかの例ではオブジェクト`Rectangle`に対して`grow_rectangle`を発動させるとコピーされた別のインスタンスは属性を受け取る。しかし、関数`rotate_rectangle`の動作では別のインスタンスと属性が影響を与える。この振る舞いは注意する。エラーの元だ。\n\n丈にして、選択子は何か調べていて、どのオブジェクトが他と異なり、また耳理にこれが異なるオブジェクト等のコピーを生成する関数`deecopy`が提供されている。これを深いコピー(deep copy)と呼ぶ理由は明白である。'}, {'id': 'ae073ffe-5e4b-4170-a6a6-7eb0d495aabc', 'page': 214, 'text': '214\n第15 章\nクラスとオブジェクト\n>>> box3 = copy.deepcopy(box)\n>>> box3 is box\nFalse\n>>> box3.corner is box.corner\nFalse\nこれでbox とbox3 は完全の別なオブジェクトである。\n練習問題15.3 引数としてRectangle オブジェクトを受け取り、新しいRctangle\nを返す関数move rectangle の改訂版を作成せよ。\n15.7\nデバッギング\nオブジェクトの処理を含むプログラムを扱い始めると新た種類のエラーに遭遇\nする。もし存在しない属性にアクセスしようとすると、AttributeError が出る:\n>>> p = Point()\n>>> print p.z\nAttributeError: Point instance has no attribute ’z’\nもし一つのオブジェクトがどうようなものか不明な時は、以下のようなコマンド\nを使うとよい:\n>>> print p\n<__main__.Point instance at 0x011FDAF8>\nもしある属性をそのオブジェクトが持っているか不明なときは組み込み関数hasattr\nが使える:\n>>> hasattr(p, ’z’)\nFalse\n>>> hasattr(p, ’x’)\nTrue\n第一番目の引数は任意のオブジェクトで、第二番目の引数はその属性とした名前\nの文字列(訳注:強調)である。\n'}, {'id': '7e57b1eb-b88e-4f48-a3e1-25230adfbb85', 'page': 215, 'text': '15.8.語句21515.8語句クラス(class):ユーザ定義型。クラスの定義によって新しいクラスオブジェクトが生成される。クラスオブジェクト(classobject):ユーザ定義型に関する情報を含むオブジェクト。クラスオブジェクトはその型のインスタンスを生成するときに使われる。インスタンス(instance):一つのクラスを具現化したオブジェクト。属性(attributes):一つのオブジェクトに付随した名前の付いた値。埋め込まれたオブジェクト(embedded\u3000object):他のオブジェクトの属性として使われたオブジェクト。浅いコピー(shallowcopy):埋め込まれたオブジェクトへの参照を残したままオブジェクトの複製を作る。深いコピー(deepcopy):そのなかに埋め込まれたオブジェクトがあるときその複製をも作るかたちでオブジェクトの複製を作る。オブジェクト状態図(objectdiagram):オブジェクト、その属性、その属性が参照している値を明示するグラフ表現。15.9練習問題練習問題15.4パッケージswampyはWorldという名前のモジュールを提供している。そこではWorldとい名前のユーザ定義型を定義している。そのモジュールを使う:fromswampy.WorldimportWorld以下のようなコードでWorldオブジェクトを生成し、メソッドmainloopでユーザ入力を待つことになる:world=World()world.mainloop()これで何もない正方形の窓が出現する。この窓に点、長方形、その他の図形を描くことにする。以下の三行をmianloopの前に追加して再度実行してみよう:\x0c'}, {'id': 'b0c268c7-d3fe-4d12-81fa-6574f55b1547', 'page': 216, 'text': '216\n第15 章\nクラスとオブジェクト\ncanvas = world.ca(width=500, height=500, background=’white’)\nbbox = [[-150, -100], [150, 100]]\ncanvas.rectangle(bbox, outline=’black’, width=2, fill=’green’)\n黒で縁取られた緑の長方形が描かれるはずだ。最初の行では白の窓が描かれたカ\nンバスが生成される。オブジェクトcanvas は長方形のような種々の形をそこに描\nくメソッドを提供している。bbox はリストで「境界のあるボックス」の意味であ\nる。第一のペアが左下の座標を第二のペアが右上の座標を示している。\n円は以下のようなコマンドで描ける:\ncanvas.circle([-25,0], 70, outline=None, fill=’red’)\n第一の座標のペアが円の中心の座標、第二の引数は円の半径を示す。これで、バ\nングラディシュの国旗のような図形が完成する。\n(http://en.wikipedia.org/wiki/Gallery_of_sovereign_state_flags参照せ\nよ)\n。\n以下は練習問題である:\n1. 一つのカンバスと一つのRectangle を引数として受け取り、そのカンバスに\nその長方形を描く関数draw rectangle を作れ。\n2. オブジェクトRectangle に新たな属性としてcolor を追加し、このcolor を\n流し込みの色とするようにdraw rectanlge を修正せよ。\n3. カンバスに点を描く関数draw point を作成せよ。\n4. クラスCircle を新たにつくり、関数draw cricle でカンバス上にいくつか\n円の描いてみよう。\n5. チェコ共和国の国旗を描くプログラムを作成せよ。ヒント:多角形の描画は\n以下のようにするとよい:\npoints = [[-150,-100],[150,150], [150,-100]]\ncanvas.polygon(points, fill=’blue’)\n利用可能な色のリストを吐き出す小さなプログラムを作成した。必要ならば以下\nからダウンロードして使え(http://thinkpython.com/code/color_list.py)\n。\n'}, {'id': 'c53d4fbe-85e8-4260-a6a4-8a890159a966', 'page': 217, 'text': '# 第16章 クラスと関数\n\nこの章のサンプルコードは [thinkpython.com/code/Time1.py](http://thinkpython.com/code/Time1.py) にあります。\n\n## 16.1 時刻\n\nユーザ定義型のもう一つの例として時刻を記録する `Time` を取り上げる。そのクラス定義は以下のようだ:\n\n```python\nclass Time:\n """ Represents the time of day.\n\n attributes: hour, minute, second\n """\n```\n\n`Time` オブジェクトを生成し、属性に値を代入してみる:\n\n```python\ntime = Time()\ntime.hour = 11\ntime.minute = 59\ntime.second = 30\n```\n\n| Time | hour | minute | second |\n|-------|------|--------|--------|\n| time | 11 | 59 | 30 |\n\n図 16.1: オブジェクト図\n\n図 16.1 にこのオブジェクトの状態図を示した。\n\n練習問題 16.1 | 関数として `Time` オブジェクトを取り、`hour:minute:second` の形式で時刻表示をする関数 `print_time` を作成せよ。ヒント:`記述子:%.2d` を使う。'}, {'id': 'bc3b794d-5f75-4d8e-b9d6-06ae9e93b716', 'page': 218, 'text': '218\n第16 章\nクラスと関数\nと整数を先頭に0 を詰めて二桁で表現する。\n練習問題16.2 引数として二つのTime オブジェクトt1、t2 をとり、もしt1 が時\n刻的にt2 の後であればTrue を返し、そうでなければFalse を返す関数is after\nを作成せよ。挑戦:if 文を使わないでやってみよう。\n16.2\n純関数\n以下では時刻の和を求める関数add time を作成することにする。ここでは二つ\nの関数が開発の途上で検討されることになる。それらは純関数と修正関数である。\nまたこの二種類の関数は単純なものから初めて徐々に複雑なものにして行く開発\n計画で原型とパッチ(prototype and patch)と呼ばれるものに対応している。\n関数add time の原型は以下のようなものだろう:\ndef add_time(t1, t2):\nsum = Time()\nsum.hour = t1.hour + t2.hour\nsum.minute = t1.minute + t2.minute\nsum.second = t1.second + t2.second\nreturn sum\nこの関数では新たにsum というTime オブジェクトを生成、属性を初期化、そのオ\nブジェクトを戻り値としている。これは引数として受け取ったオブジェクトを何\nも変えないで、しかも値を表示するとか、ユーザ入力を求めるとかの効果もない\nという意味で純関数(pure function)と呼ぶ。\nこの純関数をテストするために映画“Monty Python and the Holly Grail”の開\n始時間start というTime オブジェクトとこの映画の上演時間(1時間35分)を\n保存するduration というTime オブジェクトを生成する。\nadd time で何時にこの映画が終わるか分かる:\n>>> start = Time()\n>>> start.hour = 9\n>>> start.minute = 45\n>>> start.second = 0\n>>> duration = Time()\n>>> duration.hour = 1\n'}, {'id': '78e7685d-dac3-4455-9906-3f0e03c51830', 'page': 219, 'text': '16.3.修正関数219>>>duration.minute=35>>>duration.second=0>>>done=add_time(start,duration)>>>print_time(done)10:80:00結果の10:80:00はわれわれが欲しいものではない。問題は秒や分で足し合わせた結果60より大きくなったときの処理をしていないからである。この状況が起きたときは、余分な秒を分に「桁上げ」、余分な分を時に「桁上げ」しなければならない。これを考慮した改訂版である:defadd_time(t1,t2):sum=Time()sum.hour=t1.hour+t2.hoursum.minute=t1.minute+t2.minutesum.second=t1.second+t2.secondifsum.second>=60:sum.second-=60sum.minute+=1ifsum.minute>=60:sum.minute-=60sum.hour+=1returnsum改訂版は正しいが、コードは大きめになり初めている。次章ではこれに替わる修正の方法を示す。16.3修正関数ある場合には引数として受け取ったオブジェクトを修正する関数の使用が有益なことがある。この場合にはこの関数を呼んだ側にも修正が反映される。この機能を取り入れた関数を修正関数(modifiers)と呼ぶ。与えられた秒数だけTimeオブジェクトの時刻を増加させる関数incrementは素直に修正関数として書くことができる:\x0c'}, {'id': '327c4c85-8665-4712-b3fe-01ea3de3e294', 'page': 220, 'text': '220第16章クラスと関数defincrement(time,seconds):time.second+=secondsiftime.second>=60:time.second-=60time.minute+=1iftime.minute>=60:time.minute-=60time.hour+=1第一行目が基本演算で、残りは前にみたような特別な場合の処理である。ところでこの関数は正しいだろうか?第二の引数secondが60秒よりずうっと大きな値であるとどうなるだろうか?その場合には桁上がりは一度では足りない。つまり、Timeオブジェクトのsecond属性の値が60より小さくなるまで桁上がりを続ける必要がある。一つの解決策はif文をwhile文に置き換えることだが、効率が悪い。練習問題16.3関数incrementを正せ。繰り返しのループ無しでこれを達成せよ。一般に特に問題がなければ純関数を使い、それを使う特別な理由があるときのみ修正関数を使うことを勧める。このアプローチはいわば関数プログラミング作法(functionalprogrammingstyle)と言ってもよい。練習問題16.4関数incrementの純関数版を作成せよ。引数のTimeオブジェクトを修正する替わりに、結果を新たに生成したTimeオブジェクトとして戻せ。16.4原型と開発計画\u3000ここまで示してきたプログラム開発法は「原型とそのパッチ」と呼ばれる方法である。各関数について、まず基本的な計算をする原型を作成し、その後にこれをテストし、必要な修正を施す。この手法は問題の深い理解がまだできていないとき特に有効な方法となる。しかし、この手法は徐々に複雑化するためどれほど複雑にしたらよいのかが不明なので必要以上にコードが複雑になる傾向がある。別の手法は計画に基づいた開発手法(planneddevelopment)である。これは高いレベルの問題考察でプログラミングをより容易にしてくれる手法である。Time\x0c'}, {'id': '82ad00a6-45c8-41b6-ac9d-cd010c72ba69', 'page': 221, 'text': '16.4.\n原型と開発計画\n221\nオブジェクトに関して言えば、その洞察は60 進法を基礎にした三つの数字の問題\nで、属性second はその60 進数の一桁目の数字であり、属性minute は二桁目、属\n性hour は三桁目の数字であることを教えてくれる。関数add time やincrement\nでは桁上がりを行うことで実質的に60 進法の和を実行していたことになる。\nこのような考察は全問題に対して異なる手法が採れることを教えてくれる。つ\nまり、Time オブジェクトをコンピュータが桁上がりを自動的に処理できる10 進数\n(つまり整数)に変換してしまうことだ。\nTime オブジェクトを整数に変換は以下のようである:\ndef time_to_int(time):\nminutes = time.hour * 60 + time.minute\nseconds = minutes * 60 + time.second\nreturn seconds\nこの逆に、整数をTime オブジェクトに変換するコード(関数divmod 二つの引数\nを取り、最初の引数を第二で割った商と余りをタプルとして戻す)は以下のよう\nになる:\ndef int_to_time(seconds):\ntime = Time()\nminutes, time.second = divmod(seconds, 60)\ntime.hour, time.minute = divmod(minutes, 60)\nreturn time\nこれらの関数は期待通り動くことを確認するためにいろいろなテストをやって\nみるかもしれないが、一つの面白い実験はtime_to_int(int_to_time(z)) == z\nが成り立つことを確かめることだ。これは一貫性のテストの例にもなっている。\n関数が期待通りに動くことが確かめられたら、関数add time の作成に使って\nきる:\ndef add_time(t1, t2):\nseconds = time_to_int(t1) + time_to_int(t2)\nreturn int_to_time(seconds)\nこの版はすっきりしているし、確認が楽である。\n練習問題16.5 関数increment も同様に書き換えよ。\nある面では60 進法数と10 進法数への変換、またはその逆変換は時間について\nの観念のよる操作より難しいかもしれない。この変換はより抽象的で、日常の時\n'}, {'id': '7db61f4f-952c-4d1e-96ee-14b8cc266c7a', 'page': 222, 'text': '222\n第16 章\nクラスと関数\n間に対するわれわれの直感のほうが良いかもしれない。しかし、一度60 進法数と\nしての時間につての洞察ができ、その変換プログラムを作ってしまうと、より短\nく、読むことやデバッグがより易しく、より信頼性の高いプログラムを作ること\nができる。また、この方法を取ると機能の追加をするのが楽である。例えば、二\nつの時間の差を求める問題を考えてみよう。従来の方法では、上位の桁からの借\nりが必要になる。変換関数を使う方法ではもっと簡単で、従って正常に動くプロ\nグラムが作れる。\n16.5\nデバッギング\nTime オブジェクトはminute やsecond が0 と60 の間(0 は含み60 は含まない)\nの値を持っていて、hour が正の値であると健全である。またhour やminute の値\nは整数値であるべきだが、second は小数点数であってもよい。このような要求は\nそれが何時も満たされていることが求められるものなので、不変性(invariants)\nと呼ばれている。換言すれば、それが満たされていないと何かが間違っていると\nいうことになる。\nこの不変性をチェ\nックするようにプログラムを書くと、エラーの検出やその原因\n解明にとっての助けになる。例えば、Time オブジェクトを引数としてそれば不変\n性の一つでも破っているときはFalse を返す関数valid time が作れる:\ndef valid_time(time):\nif time.hour < 0 or time.minute < 0 or time.second < 0:\nreturn False\nif time.minute >= 60 or time.second >= 60:\nreturn Flase\nreturn True\n関数の先頭でこのチェ\nックをして引数の妥当性を調べることができる:\ndef add_time(t1, t2):\n\u3000\u3000if not valid_time(t1) or not valid_time(t2):\nraise ValueError, ’invalid Time object in add_time’\nseconds = time_to_int(t1) + time_to_int(t2)\nreturn int_to_time(seconds)\nまたはassert 文を使い、条件が満たされていないと例外を発生させることもで\nきる:\n'}, {'id': '34c2e7d2-96f6-4536-99f3-e0cb24dd1b81', 'page': 223, 'text': '16.6.\n語句\n223\ndef add_time(t1, t2):\n\u3000\u3000assert valid_time(t1) and valid_time(t2)\nseconds = time_to_int(t1) + time_to_int(t2)\nreturn int_to_time(seconds)\nassert 文は通常に実行されるコードとエラー検出用のコードを区別するので役に\n立つ。\n16.6\n語句\n原型とパッチ(prototype and patch)\n:大雑把なプログラムを書き、それをテ\nストして見つかったエラーを修正するといった開発計画。\n計画に基づいた開発手法(planned development)\n:徐々に精査する開発法や原\n型とパッチ方式より問題自体を精査して高いレベルで問題を捉えなおしより\n計画性をもった開発計画。\n純関数(pure function)\n:引数として受け取ったオブジェクトに如何なる変更も\n施さない関数。多くは戻り値を持つ。\n修正関数(modifiers)\n:引数として受け取った一つまたはそれ以上のオブジェク\nトに修正を施すような関数。\n関数プログラミング作法(functional programming style)\n:多くの関数が純関\n数として計画されたプログラミング手法。\n不変性(invariants)\n:プルグラムに実行過程で常に「真」の状態にあるべきとさ\nれる条件。\n16.7\n練習問題\nこの章で使ったコードは以下から利用できる:\nhttp://thinkpython.com/code/Time1.py。\nまた、練習問題の解答例は以下からダウンロードできる:\nhttp://thinkpython.com/code/Time1_soln.py\n練習問題16.6 Time オブジェクトと数を引数として、このTime オブジェクトにこ\nの数を乗じたTime オブジェクトを返す関数mul time を作成せよ。更にこの関数\n'}, {'id': 'f92ba8b2-01a4-427d-8995-4e9d4a82d739', 'page': 224, 'text': '224\n第16 章\nクラスと関数\nmul time を使って、レースの到着時間を表すTime オブジェクトと距離(マイル)\nを表す数を引数として、\nレースの平均ペース\n(時間毎マイル)\nを示すTime オブジェ\nクトを返す関数を作成せよ。\n練習問題16.7 モジュールdatetime はこの章で議論したと同じようなdate やtime\nオブジェクトを提供しているが、さらに多くのメソッドや操作を含んでいる。詳\nしくはdocs.python.org/lib/datetime-date.html を参照せよ。\n1. 日日を引数にとり、曜日を返す関数datetime を作成せよ。\n2. 誕生日の日付を受け取り、ユーザの年齢と次ぎの誕生日までの日数、時、分、\n秒を表示するプログラムを作成せよ。\n3. 異なった日に生まれた二人にといて、一人が他の年齢の二倍になる日が存在\nする。これは二人にとって「二倍日」である。二つの誕生日を引数として受\nけ取り、この二人の「二倍日」を計算するプログラムを作成せよ。\n4. もう少し挑戦的な問題として、一般に二人の誕生日を引数として、一人の年\n齢が他の年齢n 倍のなる日を計算するプログラムを作成せよ。\n'}, {'id': 'c1f72c8d-e910-4299-894e-49dee3743520', 'page': 225, 'text': '225第17章クラスとメソッドこの章のサンプルコードはhttp://thinkpython.com/code/Time2.pyにある。17.1オブジェクト指向の特徴Pythonはオブジェクト指向プログラミングを行う機能を提供しているという意味でオブジェクト指向言語(object-orientedprogramminglanguage)である。オブジェクト指向プログラミング(object-orientedprogramming)を定義することは易しくはないが、それらのいくつかの特徴には既に出会っている:•プログラムはオブジェクトの定義、関数の定義から構成され、多くの操作がオブジェクトに対する演算として表現される。•各オブジェクトは現実世界の実体や概念に対応していて、オブジェクトに対し適用される関数は現実世界の実体が相互作用している様に対応している。例えば、第十六章で定義したTimeクラスは人々が時間を記録する様に対応し、その関数は人々がその時間について行う操作に対応している。同様にPointクラスやRectangleクラスは数学的な概念に対応して定義される。\u3000いままではPythonが提供しているオブジェクト指向の特徴を利用してこなかった。それらの特徴は厳格には必要でない。それらの特徴の多くはこれまで作ってきたプログラムの異なった表現による解を提供するにすぎない。しかし、多くの場合、この別表現はより簡便で、より正確にプログラムの構造を伝達することができる。例えば、Timeクラスでは、クラスの定義とそれに続く関数定義との間には関連があるかどうかは自明ではない。詳しくみると、全ての関数の引数の内少なくとも一つはTimeオブジェクトであることが分かる。この観察はある特別なクラスに付随する関数、つまりメソッド(method)の導入の契機になる。これまで文字列、リスト、辞書そしてタプルのためのメソッドをみてきた。この章ではユーザ定義型に対するメソッドを定義する。メソッドは意味論的には関数と同じであるが、構文的には二つの点で関数とは異なる:\x0c'}, {'id': '2b788dca-1739-49cd-bcd1-7435a64cb46a', 'page': 226, 'text': '226\n第17 章\nクラスとメソッド\n• メソッドはクラスとの関連を明白にするため、クラスの内部で定義される。\n• メソッド起動に対する構文は関数を呼ぶ場合の構文とは異なる。\n次の数節では前二章に登場した関数を取り上げ、それらをメソッドに変換する。変\n換自体は極めて機械的である。ステップに従えばできる。この変換に問題を感じ\nないのであれば、何をするときも、二つ表現の内から最良と思うものを選択して\n使ってもらえばよい。\n17.2\nオブジェクトのprint\n第十六章ではTime クラスを定義し、練習問題16.1 ではprint time という関数\nを作成した:\nclass Time(object):\n"""Represents the time of day."""\ndef print_time(time):\nprint ’%02d:%02d:%02d’ % (time.hour, time.minute, time.second)\nこのprint time 関数を呼ぶためには引数にTime オブジェクトを渡さなければな\nらない:\n>>> start = Time()\n>>> start.hour = 9\n>>> start.minute = 45\n>>> start.second = 0\n>>> print_time(start)\n09:45:00\nprint time をメソッドにするために、しなければならないことの全てはこの関数\n定義をクラス定義の中に移動することだ(インデントの調整も忘れずに)\n:\nclass Time(object):\ndef print_time(time):\nprint ’%02d:%02d:%02d’ % \\\n(time.hour, time.minute, time.second)\nさて、このprint time を呼ぶ方法には二通りある。最初は(あまり普通はやら\nないが)関数呼び出しの構文である:\n'}, {'id': 'f7e44e44-ea05-4b34-b0fe-76b353790eb2', 'page': 227, 'text': '17.2.\nオブジェクトのprint\n227\n>>> Time.print_time(start)\n09:45:00\nこのドット表記では、Time はクラス名、print time はメソッド名、start は引数\nである。\n第二は(普通にやる)メソッド構文によりものだ:\n>>> start.print_time()\n09:45:00\nこのドット表記では、print time はメソッド、start はこのメソッドを発動させ\nるオブジェクトで、主語(subject)と言う。文章の主語がその文章が何について\nであるか示すと正に同じように、メソッドを発動する主語はそのメソッドが何に\nついてあるかを示すものである。\nメソッドの中では主語は第一番目の仮引数として代入される。今の場合はstart\nが仮引数time に代入される。慣例で、メソッドの第一仮引数はself という名前が\n使われる。したがって、より通常の形にprint time を書けば以下のようになる:\ndef print_time(self):\nprint ’%02d:%02d:%02d’ % \\\n(self.hour, self.minute, self.second)\nこの慣例で書く理由は直接的な比喩にある:\n• 関数呼び出しprint time(start) の構文は関数が活動主体であると示唆し\nている。差詰め、\n「さあ、print time よ。ここにお前さんがプリントするオ\nブジェクトが一つあるよ」と言っているようなものだ。\n• オブジェクト指向プログラミングでは、オブジェクトが活動主体である。\nstart.print time() のようにメソッドを起動することは、\n「やあ、start。\n自分のことは自分で処理してください」と言っているようなものだ。\nこのような見方の変更はより洗練されたものかもしれないが、それが有用である\nことは自明ではない。これまでの例では有用とはなっていない。しかし、責任主\n体を関数からオブジェクトに移すことは時として用途が多い関数を書き、よりメ\nンテナンスが容易で再利用可能な関数を書くことを可能にするのだ。\n練習問題17.1 関数time to int(16.4 節)をメソッドとして書き換えよ。関数\nint to time はメソッドに書き換える理由がない。これを発動させるオブジェクト\nがあるかな?\n'}, {'id': '31ebb4c9-6330-41c8-b6ff-7cd4b98a8242', 'page': 228, 'text': '228\n第17 章\nクラスとメソッド\n17.3\n別な例\n16.3 節で登場した関数increment をメソッドとして書き換える:\n# クラスTime の定義に中で\ndef increment(self, seconds):\nseconds += self.time_to_int()\nreturn int_to_time(seconds)\nここでtime to int は練習問題17.1 のようにメソッドとして書かれているものと\nする。さらに、このメソッドは純関数をメソッドにしたもので、修正関数ではな\nいことにも注意すること。\nこのincrement の発動は以下のようになる:\n>>> start.print_time()\n09:45:00\n>>> end = start.increment(1337)\n10:07:17\n主語startが関数の第一仮引数selfに代入される。\n引数1337は第二仮引数seconds\nに代入される。\nこの機構は特にエラーに遭遇したときに混乱の元になる。例えば、increment\nに二個の引数を与えて発動させると以下のようにエラーになる:\n>>> end = start.increment(1337, 460)\nTypeError : increment() takes exactly 2 arguments (3 given)\nこのエラーメッセージには最初混乱する。括弧の中は二つの引数になっているか\nらだ。しかし、主語それ自体も引数として考えるので、合わせて三個ということ\nになる。\n17.4\nもっと複雑な例\n関数is after(練習問題16.2)は引数に二つのTime オブジェクトを取るから\n少し複雑になる。この場合最初の仮引数の名前はself、二番目をother とするの\nが慣例である:\n# クラスTime の定義に中で\ndef is_after(self, other):\nreturn self.time_to_int() > other.time_to_int()\n'}, {'id': '43646cdc-61fb-402b-abc9-7edabecd595f', 'page': 229, 'text': '17.5.initメソッド229このメソッドを使うには、一つのオブジェクト上でこれを発動させ、他のオブジェクトは引数としてこのメソッドに渡す:>>>end.is_after(start)Trueこの構文の愉快なのは英文を読むような順序に並んでいることだ、つまり、“endisafterstart?”。17.5initメソッドinitメソッド(“initialization”の省略)はオブジェクト具現化の際に発動される特殊なメソッドである。正式名は__init__(二つのアンダースコア記号にinitが続き更に二つのアンダースコア記号)。Timeクラスのinitメソッドは以下のようなものだろう:#クラスTimeの定義に中でdef__init__(self,hour=0,minute=0,second=0):self.hour=hourself.minute=minuteself.second=second__init__の引数の名前を属性と合わせることは習慣だ。文self.hour=hourの意味は引数hourの値はselfの属性(cid:3)hourの値として保存されるということである。>>>time=Time()>>>time.print_time()00:00:00もし引数一つを与えると、その値はhourの値を再定義する:>>>time=Time(9)>>>time.print_time()09:00:00もし引数二つを与えると、それらはhour,minuteの値を再定義する:>>>time=Time(9,45)09:45:00引数三つを与えると既定値の全てが無効になる。練習問題17.2Pointクラスのinitメソッドを作れ。引数はxとyで、対応する属性に代入されるようにせよ。\x0c'}, {'id': 'cdb65159-2739-409a-a211-ddace1dd34d1', 'page': 230, 'text': '230\n第17 章\nクラスとメソッド\n17.6\nstr メソッド\n__str__メソッドはオブジェクトの文字列による表現を返す特殊なメソッドで\nある。\nTime オブジェクトのためのstr メソッドは以下のようなものだ:\n# クラスTime の定義に中で\ndef __str__(self):\nreturn ’%02d:%02d:%02d’ % \\\n(self.hour, self.minute, self.second)\nTime オブジェクトをprint しようとすると、str メソッドが発動される:\n>>> time = Time(9, 45)\n>>> print time\n09:45:00\nわたしが新しいクラスを書くときは、\n殆ど常にメンテナンスの容易さから__init__\nメソッドを書き、デバッギングに有用なので__str__メソッドを書くことにしてい\nる。\n練習問題17.3 Point クラスのstr メソッドを作成し、Point オブジェクトを一つ\n生成し、それをprint せよ。\n17.7\n演算子の多重定義\n特殊なメソッドの定義によって、ユーザ定義型のオブジェクトに対する演算の\n振る舞いを変更することができる。例として、Time クラスの__add__メソッドで\n加算演算子+を定義してみよう:\n# クラスTime の定義に中で\ndef __add__(self, other):\nseconds = self.time_to_int() + other.time_to_int()\nreturn int_to_time(seconds)\nこれを使ってみる:\n>>> start = Time(9, 45)\n>>> duration = Time(1, 35)\n>>> print start + duration\n11:20:00\n'}, {'id': '0b057ac1-31bc-42d9-bc18-486490b7ce77', 'page': 231, 'text': '17.8.\n型別処理\n231\nTime オブジェクトの+演算子を使うとPython はこの__add__を発動する。結果を\nprint しようとすると、__str__が発動される。\nユーザ定義型のオブジェクトに対して演算子の振る舞いを変更することを演算\n子の多重定義(operator overloading)と言う。\n練習問題17.4 Point クラスのオブジェクトに対するadd メソッドを作成せよ。\n17.8\n型別処理\n前の節では二つのTime オブジェクトを被演算子とするadd メソッドを作成した\nが、一つのTime オブジェクトに整数型の数を加えることも考えられる。以下は引\n数other の型をチェ\nックする__add__メソッド改訂版で、add time か、increment\nかの発動に繋げる:\n# クラスTime の定義に中で\ndef __add__(self, other):\nif isinstance(other, Time):\nreturn self.add_time(other)\nelse:\nreturn self.increment(other)\ndef add_time(self, other):\nseconds = self.time_to_int() + other.time_to_int()\nreturn init_to_time(seconds)\ndef increment(self, seconds):\nseconds += self.time_to_int()\nreturn int_to_time(seconds)\n組み込み関数isinstance は値とクラスオブジェクトを引数として受け取り、そ\nの値がそのクラスのインスタンスであるとTrue を返す。もしother がTime オブ\nジェクトであると、__add__メソッドはadd time を発動させる。そうでないとき\nはincrement を発動させる。引数の型によって異なった計算処理を行うのでこの\nような演算を型別処理(type-based dispatch)と呼ぶ。\n異なった型の演算に使ってみる:\n>>> start = Time(9, 45)\n'}, {'id': 'dde1373b-2f6c-4532-af98-e777702bd96e', 'page': 232, 'text': '232第17章クラスとメソッド>>>duration=Time(1,35)>>>printstart+duration11:20:00printstart+133710:07:17不幸にしてこの加算演算は交換可能でない。整数を被演算子の最初に置くとエラーになる。>>>print1337+startTypeError:unsupportedoperandtype(s)for+:’int’and’instance’問題はTimeオブジェクトに整数を加えることを要請する替わりに、Pythonは整数にTimeオブジェクトを加算することを要請することになり、どうしてよいか分からなくなっていることである。しかし、上手い解決策がある。__radd__(“rightsideadd”の略)は特殊なメソッドでTimeオブジェクトが演算子+の右側にあるときにこのメソッドが発動される。今の場合の定義は以下のようになる:#クラスTimeの定義に中でdef__radd__(self,other):returnself.__add__(other)使ってみる:>>>print1337+start10:07:17練習問題17.5クラスPointでPointオブジェクトでもタプルでも有効なaddメソッドを作成せよ。•第二被演算子もPointオブジェクトであるときには、戻り値はその被演算子のx座標の和、y座標の和を要素とする新規のPointオブジェクトになる。•第二被演算子がタプルのときにはタプルの初めの要素をx座標に加え、第二の要素をy座標に加えた新たなPointオブジェクトを戻す。17.9多態性型別処理は必要なときは便利にものだが、常に必要になる訳ではない。例として11.1節で取りあげたhistogramでは一つの単語の中の文字頻度をカウントするのに使った。\x0c'}, {'id': '2e0b1fab-6175-4b51-9766-dc66ef9b0e66', 'page': 233, 'text': '17.10.デバッギング233defhistogram(s):d=dict()forcins:ifcnotind:d[c]=1else:d[c]+=1returndこの関数はその要素がハッシュ可能ならば、つまりキーとして使えるならば、引数はリスト、タプル、それに辞書でさえ有効だ。以下はリストの例である:>>>t=[’spam’,’egg’,’spam’,’spam’,’bacon’,’spam’]>>>histogram(t){’bacon’:1,’egg’:1,’spam’:4}いくつかの型に対しても有効な関数は多態的(polymorphic)だと言う。多態性はコードの再利用に役立つ。例えば、組み込み関数sumは配列の要素の総和を求める関数であるが、加算演算子が定義されている要素であれば、如何なる配列でも構わない。>>>t1=Time(7,43)>>>t2=Time(7,41)>>>t3=Time(7,37)>>>total=sum((t1,t2,t3))>>>printtotal23:01:00一般に関数内の全ての操作がある型に対して有効であるならば、その関数はその型に対して有効である。多態性の面白さは既に作成した関数が当初計画しないでいた型に対しても有効だというのを発見すると言った予期しないことがあることだ。17.10デバッギングクラスの属性はプログラムの実行中に任意に追加することができるが、もし型に対して厳格な考え方を持つなら、同じ型のオブジェクトが異なった属性を持つことは疑惑に満ちた習慣だ。オブジェクトの初期化の際に必要な属性を初期化し\x0c'}, {'id': 'ab2ed005-0a06-4032-899b-75c5b107aa16', 'page': 234, 'text': '234\n第17 章\nクラスとメソッド\nておくことは健全であろう。あるオブジェクトがある特定の属性を持っているか\nどうかを調べるには組み込み関数hasattr が役に立つ(15.7 節を参照のこと)\n。\nあるオブジェクトの属性にアクセスするもう一つの方法は特殊な属性__dict__\nを使うことだ。\nこれはこのオブジェクトの属性とその値を辞書として保持している。\n>>> p = Point(3, 4)\n>>> print p.__dict__\n{’y’: 4, ’x’: 3}\nこの機能をデバッギングに使うために、簡便な関数を作成する:\ndef print_attributes(obj):\nfor attr in obj.__dict__:\nprint attr, getattr(obj, attr)\nprint attributes はオブジェクトの辞書を横断的に眺め、その属性と対応する値\nを表示する。組み込み関数getattr はオブジェクトと属性を引数に取り、属性の\n値を返す関数である。\n17.11\nインタフェースと実装\nオブジェクト指向によるプログラム設計の一つの目標はソフトウエアをより管\n理・維持し易くすることである。この意味するところはシステムの他の部分の変\n更があってもプログラムが動くようにしておくことができ、且つ、その変更に適\n合するようにプログラムを変更できることだ。\nこの目標を達成するための設計原理はインタフェースと実装を分離して置くこ\nとである。オブジェクトに関して言えば、クラスが提供するメソッドはその属性\nが如何に表現されるかに依らないようにすべきである。\n例えば、この章では時刻を表現するクラスを作成した。このクラスが提供する\nメソッドはtime to int、is after、そしてadd time である。これらのメソッ\nドはいくつかの方法で実装できる。実装の細部は時刻の表現の仕方に依存してい\nる。この章では、Time オブジェクトのhour, minute, second という属性がそれで\nある。他の表現方法として、深夜12 時からの経過時間を秒にした一つ整数で表現\nすることもできる。この実装に沿っていくつかのメソッドが作られることになる。\nis after などは容易に書けるメソッドだ。書くのが難しいメソッドもあるだろう。\n新たなクラスの展開に伴って、もっと改良された実装を見つけるかもしれない。\nプログラムの他の場所でこのクラスを使っていたとすると、この実装変更に伴って\nインタフェースの変更が伴うとするとこれは時間の浪費とエラーの元になる。し\n'}, {'id': 'e1d2bf89-2654-4e29-8612-a16d9e59ecc9', 'page': 235, 'text': '17.12.\n語句\n235\nかし、インタフェースを注意深く設計して置くとインタフェースの変更無しに実\n装の変更ができる。つまり、プログラムの他の部分への変更はナシで済む。\nインタフェースを実装から分離しておくということは属性を隠せということだ。\nプログラムの他の部分(クラス定義の外)では、メソッドを使いオブジェクトの\n状態を読み、変更を行うようにするのだ。属性を直接にアクセスしないようにす\nるわけだ。このような原理を情報隠蔽(information hiding)と言う。\n([http://en.wikipedia.org/wiki/Information_hiding を参照のこと。\n)\n練習問題17.6 http://thinkpython.code/code/Time2.py をダウンロードせよ。\nそしてTime オブジェクトの属性を真夜中からの経過時間(秒)を表す一つの整数\nに変更せよ。そして、この変更に見合うようにメソッド\n(そして関数int to time)\nを変更せよ。main は変更してはいけない。このプログラムを実行すると変更前の\n表示と同じものがでるはずである。\n解答例:http://thinkpython.com/code/Time2_son.py\n17.12\n語句\nオブジェクト指向言語(object-oriented programming language)\n:ユーザ定\n義クラスやメソッド構文のようなオブジェクト指向プログラミングをサポー\nトする特徴を持っている言語。\nオブジェクト指向プログラミング(object-oriented programming)\n:データと\nそれを操作する命令をクラスとメソッドとして組織化するプログラミング・\nスタイル。\nメソッド(method)\n:クラス定義の中で定義される関数そしてそのクラスのイン\nスタンスによって発動される。\n主語(subject)\n:メソッドを発動するオブジェクト(実体)\n。\n演算子の多重定義(operator overloading)\n:ユーザ定義型に直に有効なように\n+のような演算子の振る舞いを変える。\n型別処理(type-based dispatch)\n:被演算子の型を判定し異なった型には異なっ\nた関数を当てはめるプログラミング手法。\n多態的(polymorphic)\n:一つ以上のデータ型に適用できるようになっている関\n数の性格。\n'}, {'id': 'ddc0ef41-f573-49fc-a5fb-ecfeb0d72c9b', 'page': 236, 'text': '236\n第17 章\nクラスとメソッド\n情報隠蔽(information hiding)\n:一つのオブジェクトが提供するインタフェー\nスがその実装特にその属性の表現に依存しないようにするという原理。\n17.13\n練習問題\n練習問題17.7 この練習問題はPython でよく遭遇するが、しかし発見が難しいエ\nラーについての教訓物語である。以下のようなメソッドを持つKangaroo と言うク\nラスを書け。\n1. 属性pouch contents を空のリストで初期化する__init__メソッド。\n2. 任意の型を引数に取りそれをpouch contents に追加するput in pouch メ\nソッド。\n3. kangarooオブジェクトの文字列による表現とポーチの中味を表示する__str__\nメソッド。\nコードのテストとして二つのkangaroo オブジェクトを生成し、kanga とroo とい\nう変数に代入し、一つのオブジェクトkanga のポーチにroo を追加してみよう。\n以下をダウンロードせよhttp://thinkpython.com/code/BadKagaroo.py。こ\nれは前問に含まれる大きなしかもたちの悪いバグを含んだ解答例である。そのバ\nグを修正せよ。立ち往生してしまったら、解答例を参照しよう。\n解答例http://thinkpython.com/code/GoodKanagroo.py\n練習問題17.8 三次元グラフィ\nックスを提供するPython のモジュールVisual があ\nる。標準のPython パッケージに含まれていないが、最寄りのソフトウエア資源サ\nイトからか、またはpyhton.org からダウンロードできる。\n以下の例では縦・横・高さが256 ユニットの三次元空間を生成し、(128,128,128)\nの点を中心となす球を描画する:\nfrom visual import *\nscene.range = (256, 256, 256)\nscene.center = (128, 128, 128)\ncolor = (0.1, 0.1, 0.9) # mostly blue\nsphere(pos=scene.center, radius=128, color=color)\n'}, {'id': '1dcaaff2-d07f-4421-97d1-51007cc2d13b', 'page': 237, 'text': '17.13.\n練習問題\n237\ncolor は赤、緑、青の値を0.0 から1.0 までの範囲に持つRGB を要素にするタプル\nである(色モデルはhttp://en.wikipedia.org/wiki/RGB_color_model を参照\nのこと)\n。マウスの中ボタンを押してマウスを上下にドラックすると球を拡大・縮\n小することができる。さらに右ボタンでシーンを回転させることもできるが、球\n一つのシーンでは確認が難しい。\n以下のループは沢山の球で立方体を作る:\nt = range(0, 256, 51)\nfor x in t:\nfor y in t:\nfor z in t:\npos = x, y, z\nsphere(pos=pos, radius=10, color=color)\n1. これをスクリプトとして作成し、期待通りに動くことを確認せよ。\n2. 三次元の座標値をRGB の値として色を設定せよ。\n三次元の座標値は0 から255\nであるが、RGB 値は0.0 から1.0 までであることに注意。\n3. http://thinkpython.com/code/color_list.py をダウンロードし、\n関数read colors を使い使用可能な色彩名とRGB 値のリストを作成し、RGB\n値に対応する位置にその色の球を描け。\n解答例:http://thinkpython.com/code/color_sphere.py\n'}, {'id': 'b6719cd6-6fe1-4ef4-ae38-5a9630a8988d', 'page': 1, 'text': ''}, {'id': '67cc0fa4-8c68-4e6b-915f-a540c0720d8a', 'page': 239, 'text': '239第18章継承この章ではトランプカードゲーム、カードの組み、ポーカーの手と言った表現のためのクラスを取りあげる。ポーカーゲームを知らない読者はhttp://en.wikipedia.org/wiki/Pokerを参照のこと。練習問題を行うのに必要な知識はその都度示すから知らなくても構わない。この章のサンプルコードはhttp://thinkpython.com/code/Card.pyからダウンロードできる。アングロ・アメリカンたちのカードゲームについて馴染みがないならば、http://en.wikipeida.org/wiki/Playing_cardsを参照のこと。18.1カードオブジェクト\u3000一組のトランプカードは4つのスートと13のランクからなる52枚のカードで構成される。スートはブリッジの得点順序に従うとスペード、ハート、ダイアモンド、クラブがあり、ランクはエース、2,3,4,5,6,7,8,9,ジャック、クイーン、キングがある。ゲームに依るが、エースはキングより点が高いこともあれば、2より低い場合もある。この一枚のトランプカードを表現する新規のオブジェクトを定義したいとすると、それが持つべき属性はrankとsuitであることは自明である。しかし、型についてはそうでもない。一つの可能性として、スートでは’Spade’というような、ランクでは’Queen’といいうような文字列だ。実装に伴う問題として、どちらのカードが上位かなどを計算することが文字列では難しい。他の可能性はランクやスートを符号化(encode)した整数を使うことである。ここで「符号化」の意味するところはスートと数、ランクと数の写像を定義することである。この種の符号化は(暗号のような)秘密であることは意味しない。以下のテーブルはスートと対応する整数を示した:Spades(cid:5)→3Hearts(cid:5)→2Diamonds(cid:5)→1Clubs(cid:5)→0\x0c'}, {'id': 'a4da96fe-7717-45c4-a144-83eb2be917a4', 'page': 240, 'text': '240\n第18 章\n継承\n高い得点のスートが大きな数に写像されているのでこの符号化はカードの比較が\n容易だ。ランクの写像はかなり自明で、ランクの数字を対応する数に写像すれば\nよい、そして顔カードの写像は以下のようにする:\nJack\n\x05→\n11\nQueen\n\x05→\n12\nKing\n\x05→\n13\nここで写像にはPython のコードの一部でないことを明瞭にするために記号を使っ\nた。これはプログラム設計の一部であり、コードには直接には現われない。カー\nドオブジェクトは以下のようなものになる:\nclass Card(object):\n"""Represents a standard playing card. """\ndef __init__(self, suit=0, rank=2):\nself.suit = suit\nself.rank = rank\nいつものように、init メソッドは各属性を選択機能つき引数として受け取る。既\n定値はクラブの2 である。\n18.2\nクラスの属性\nヒトが理解できるかたちでカードを表示しようとすると、整数コードと対応す\nるスートとランクの写像が必要になる。その自然なかたちは文字列のリストであ\nる。このリストをクラス属性(class attributes)として設計しよう:\n# クラスCard の定義に中で\nsuit_names = [’Clubs’, ’Diamonds’, ’Hearts’, ’Spades’]\nrank_names = [None, ’Ace’, ’2’, ’3’, ’4’, ’5’, ’6’, ’7’,\n’8’, ’9’, ’10’, ’Jack’, ’Queen’, ’King’]\ndef __str__(self):\nreturn ’%s of %s’ % (Card.rank_names[self.rank], \\\nCard.suit_names[self.suit])\nsuit names やrank names のようにクラスの内部で定義され、クラス外の任意の\nメソッドから参照できる変数はそのクラスに付随しているのでクラス属性(class\n'}, {'id': '398753d4-095d-48b3-b09f-6ea16cccbdf3', 'page': 241, 'text': '```markdown\n# 18.3 カードの比較\n\n`attributes` と呼ばれる、`rank` や `suit` の変数はインスタンスに付随しているのでインスタンス属性 (instance attributes) と呼ばれる。\n\n同様の性質はメソッド内で指定できる。例えば、`__str__` メソッド内では、`self` のカードオブジェクトであり、`rank` はそのランクを表す。同時に `Card` はクラスオブジェクトであり、`Card.rank_names` はそのクラスに付随している文字列のリストである。各カードオブジェクトは自身の `rank` と `suit` を持つが、`suit.names` や `rank.names` は共通に一つである。\n\nまとめると、`Card.rank_names[self.rank]` の表現の意味は「配列のインデックスとしてクラスオブジェクト `self` の属性 `rank` をクラス `Card` の `rank_names` リストに代入し、ランクの名前として適当な文字列を選ぶ」ということである。\n\nこれでメソッドを使うと、カードを生成し、そのカードをプリントすることができる。\n\n```\ncard1 = Card(2, 11)\nprint(card1)\n```\n\n図 18.1 は `Card` クラスオブジェクトと 1 枚のカードの実体を示す `Card` インスタンスの図である。`Card` はクラスオブジェクトであるので、`card1` のラベルは `Card` である。\n\n```\nFigure 18.1: オブジェクト図\n```\n\n## 18.3 カードの比較\n\n組み込み型に関しては比較演算子 (`<`, `<=`, `>`, `>=`, `==`, `!=` など) がより大きい、より小さい、他と等しいなどの個別の比較に使える。ユーザ定義型については、`__cmp__` メソッドが提供する組み込み型の操作を再定義することになる。\n```'}, {'id': '9cbc7d14-6900-4b2c-933a-90905320af52', 'page': 242, 'text': '242第18章継承メソッド__cmp__は二つの引数selfとotherを受け取り、第一オブジェクトが第二より大であると正の数を返し、小であると負の数、等しいと0を返す。カードの順位は自明ではない。例えば、クラブの3とダイアモンドの2ではどちらが上か?一つは高いランクを持つが、他はスートでは上である。比較を可能にするためには、ランクとスートのどちらが重要かを決めないといけない。多分この答えは実際のゲームのルールによる。しかし、ここでは問題を簡単にするために、スートはより重要だと恣意的だが設定しよう。従って、スペードの全てのカードはどんなダイアモンドより高位になる、そして以下同様である。このように決めると比較演算子のメソッド__cmp__を書くことができる。#クラスCardの定義に中でdef__cmp__(self,other):#まずスートを調べるifself.suit>other.suit:return1ifself.suitother.rank:return1ifself.rank>>deck=Dec()\x0c'}, {'id': '91954736-c7d8-429b-99d4-96e27ad4c56e', 'page': 244, 'text': '244第18章継承>>>printdeckAceofClubs2ofClubs3ofClubs………10ofSpadesJackofSpadesQueenofSpadesKingofSpades結果は52行の表示であるが、これは改行文字を含む一つの長い文字列である。18.6追加・移送・シャッフル・ソートカードの扱いとして欲しいメソッドとしては、カードの山から一枚のカードを移送する、及び追加するがある。第一のメソッドに対して、popが便利な方法を提供できる:#クラスDeckの定義に中でdefpop_card(self):returnself.cards.pop(popはリストの最後の要素を削除するので、積み札の底からカードを抜き取ることになる。実際のゲームでは「底から抜き取り」は眉をひそめる行為であるが、今の場合はよしとする。カードの追加はappendを使う:#クラスDeckの定義に中でdefadd_card(self,card):self.cards.append(card)このような大したこともしないで他の関数を使うメソッドはベニヤ(veneer)と呼ばれる。これは良質の木材の薄い層を張り合わせて廉価な木材を作り出す木工技術の比喩からきている。ここでは積み札に対して妥当なリストの処理を「薄い」メソッドとして定義している。シャッフルも同様にして、randomモジュールのshuffle関数を使ってshuffleメソッドをDeckの内部に作る:\x0c'}, {'id': 'dd797ede-54bb-4d38-8c45-d3fc10ec5a5f', 'page': 245, 'text': '18.7.\n継承\n245\n# クラスDeck の定義に中で\ndef shuffle(self):\nrandom.shuffle(self.cards)\nrandom モジュールのインポートを忘れないように。\n練習問題18.2 Deck のもう一つのメソッドsort をリストのメソッドsort を使って\n作成せよ。メソッドsort はソートの順位を決めるために__cmp__メソッドを使う。\n18.7\n継承\nオブジェクト指向言語に付随する大きな言語的な特徴は継承(inheritance)で\nある。継承は既存のクラスを改変することで新しいクラスを生成する能力のこと\nだ。新しいクラスは既存のクラスのメソッドを引き継ぐので、これは「継承」と\n呼ばれている。この比喩を拡張して、既存のクラスは親クラス(parent class)、\n新しいクラスは子クラス(child class)と呼ばれる。\n例として、一人のカードプレーヤが保持しているカード、つまり持ち札を考え\nてみよう。持ち札は積み札に似ている。どちらもカードの集合からなっていて、追\n加や移送の操作を必要とする。\n手札は積み札と異なる面もある。積み札では意味を成さないような操作が手札\nについては必要になることがある。例えば、ポーカーではどちらが勝者かを決め\nるために二人の手札を比較する必要がある。また、ブリッジでは賭けをするため\nに、手札のスコアを計算する必要がある。\nこのようなクラス間の異同はそれ自体で継承という概念に繋がっている。子のク\nラスの定義は他のクラスの定義と似ているが、親クラスの名前が括弧の中に入る:\nclass Hand(Deck):\n"""Represents a hand of playing cards."""\nこの定義ではHand クラスはDeck を継承している。\nつまり、Deck クラスのメソッド\npop card やadd card がHand でも使えるわけだ。Hand はDeck クラスの__init__\nメソッドも継承できるが、それは欲しいものではない。52 枚のカードを手札とす\nる替わりにinit メソッドは空のリストで始めることが似つかわしい。以下はHand\nクラスのinit メソッドであるが、これはDeck クラスのそれを再定義することに\nなる:\n# クラスHand の定義に中で\ndef __init__(self, label=’’):\n'}, {'id': '81bb5149-02a3-4443-a74e-b447442ff323', 'page': 246, 'text': '246\n第18 章\n継承\nself.cards = []\nself.label = label\n従って、一つの手札を生成するときにはPython はこのinit メソッドを発動する:\nhand = Hand(’new hand’)\nprint hand.cards\n[]\nprint hand.label\n#訳注Hand クラスのインスタンスhand の属性label の印刷\nnew hand\nしかし、その他のメソッドは継承するので、pop card やadd card はここでも使\nえる:\n>>> deck = Deck()\n>>> card = deck.pop_card()\n>>> hand.add_card(card)\n>>> print hand\n#訳注Hand クラスのインスタンスhand そのものの印刷\nKing of Spades\nこの手続きをDeck のメソッドmove card としてカプセル化することは自然なこと\nである:\n# クラスDeck の定義に中で\ndef move_cards(self, hand, num):\nfor i in range(num):\nhand.add_card(self.pop_card())\nmove cards は引数を二つ持ち、一つはHand オブジェクトそして操作するカード\nの枚数num である。\nカードゲームのいくつかではカードの移動は手札から手札、手札から積み札へ\n戻すなどがあるが、このmove cards を使えばよい。self はDeck でもHand でも\nよい。更に仮引数はhand となっているけれど、これはDeck でもよい。\n練習問題18.3 Hand を生成し、各Hand に決められた数の手札を配るdeal hands\nメソッドをDeck クラス内に書け。このメソッドは引数を二つ持つ、一つは生成す\nるHand オブジェクトの数、他は配る手札の数である。\n継承は有用な機能だ。継承なしては繰り返しになってしまうプログラムも継承\nによって綺麗にかけることがある。継承は親クラスを変更することをしないでも\n'}, {'id': '44fe4213-3075-4b66-b6aa-9bab167c669f', 'page': 247, 'text': '18.8.クラス図247親クラスの振る舞いをカスタマイズできるので、コードの再利用の機能を果たす。あるケースでは、継承は問題自体が持つ性質を反映することがあり、プログラムをより読みやすくする。一方、継承はプログラムを読みにくくすることもある。あるメソッドが発動されたとき、その定義がどこにあるのかが明白でないこともある。関連するコードがいくつかのモジュールに渡って散乱していることもあるうる。継承を使ってなし得る多くのことがそれ無しでも充分にやれることもある。18.8クラス図これまでプログラムの状態を示すスタック図、オブジェクトの属性とそれらの値の関係を示したオブジェクト図をみてきた。これらの図はプログラム進行中のスナップショットであり、プログラムの進行に連れて変わる。それらは極めて詳細を究め、ある目的には詳細過ぎる。クラス図(classdiagram)はプログラムの構造をより抽象的に表現したものである。個々のオブジェクトを示すかわりに、クラスとクラス間の関係を示す。複数のクラスの間にある関係としては以下のものがあり得る:•一つのクラス・オブジェクトが他のクラスのオブジェクトの参照を含む。例えば、個々のRectangleオブジェクトはPointオブジェクトの参照を含んでいるし、個々のDeckオブジェクトは多くのCardオブジェクトの参照を含んでいる。この関係は「一つのRectangleは一つのPointを持っている」にあるようにHAS-A関係(HAS-Arelationship)と呼ぶ。•一つのクラスが他のクラスを継承する。この関係は「HandはDeckの一種である」にあるようにIS-A関係(IS-Arelationship)と呼ぶ。•一つのクラスの変更が他のもう一つのクラスの変更を要求するといった依存関係にある二つのクラス間の関係。クラス図はこれらの関係を図式したものだ。例えば、図18.2はCard、Deck、Hand間の関係を示したものだ。白抜きの矢印はIS-Aの関係を表示している。この図ではHandはDeckを継承していることを示している。普通の矢印はHAS-Aの関係を表示している。この図ではDeckはCardの参照を持っていることを示している。矢印の上部のスター(*)は重複度(multiplicity)である。これはDeckクラスには何個のCardオブジェクトの参照があるかを表示するものである。これは単に52といった単なる数字、5..7と言った区間、任意の数を示すスターなどでよい。この図ではDeckは任意の数のカードを持ち得るのでスターになっている。\x0c'}, {'id': 'ce88b73d-f826-49b9-be9d-68933fb02915', 'page': 248, 'text': "# 第18章 継承\n\n![クラス図](図18.2.クラス図)\n\nもっと詳細に渡る図にしようとすれば、`Deck`オブジェクトはカードのリストを含んでいることを示すことになりながら、クラス図にはリスト、辞書などの組込み型は含まれない。\n\n練習問題18.4 `TurtleWorld.py`, `World.py` そして `GUI.py` のソースコードを読み、それらで定義されているクラス間のクラス図を描け。\n\n## 18.9 デバッギング\n\n継承はあるオブジェクトのあるメソッドの発動がどちらのメソッドの発動が不明であるということがあるので、デバッギングは問題をはらんでくる。\n\n1. は `Hand` オブジェクトに付随した関数を書こうとしたとする。その関数はポーカーの手札、ブリッジの手札などのような手札を有効だと考えたと思うとしよう。こうすると、例えば `shuffle` というメソッドを発動させたときに、`Deck`で定義されたもので、そのクラスが再定義されたものの1つかもしれない。このように、どのメソッドが発動されたかわからなくなってしまう。この簡単な修正は末尾のメソッドの原型に `print` 文を追加しておくことだと。さらに `Deck.shuffle` が呼び出されると、`Running Deck.shuffle` というメッセージが出力されるであろう。これで実行の流れが追跡できる。\n\n別な方法はオブジェクトとメソッド名(文字列)を引数に取り、そのメソッドを定義しているクラスを返す以下の関数を使うことだ:\n\n```python\ndef find_defining_class(obj, meth_name):\n for ty in type(obj).mro():\n if meth_name in ty.__dict__:\n return ty\n```\n\n### 例:\n\n```\n>>> hand = Hand()\n>>> print(find_defining_class(hand, 'shuffle'))\n```"}, {'id': '696ac193-4c37-431b-ad0b-5ea674c4e47c', 'page': 249, 'text': '18.10.\nデータカプセル化\n249\n\u3000(訳注:モジュール名がCard の場合である)\n従ってHand のshuffle メソッドはクラスDeck で定義されたものを使っているこ\nとが分かる。関数find defining class ではメソッドが探索されるクラスのリス\nトを得るメソッドmro が使われている(“MRO”は“method resolution order”の略\nである)\n。\n以下はプログラム設計に際しての示唆である。何らかの理由でメソッドを書き\n換える必要があるときにはいつでも、そのインタフェースは古いものと同じにす\nべきである。同じ型の仮引数で、戻り値も同じ型にする。つまり、同じ事前条件と\n同じ事後条件に従うようにする。この条件に従ってプログラムを設計すると、上\n位クラス(例えばDeck)のインスタンスで有効な関数を作っておくと、この関数\nはそのサブクラス(例えばHand やPokerHand)のインスタンスでも有効に使える\nことになる。このルールに違反すると、そのコードはカードで作った家のように\n簡単に崩壊する。\n18.10\nデータカプセル化\n第十六章では「オブジェクト指向設計」とも呼ぶべきプログラム開発設計の過\n程を示した。まず必要な実体、Time、Point、Rectangle を確定した。そして、こ\nれらを表現するためのクラスを定義した。各々において、オブジェクトと現実世\n界の実体との明白な対応関係(少なくとも数学的世界の)がある。\nしかし、どうようなオブジェクトが必要で、それらがいかに相互作用するのか\nが明白でない場合も往々にしてある。このような場合には別の方法を取る必要が\nある。関数のインタフェースはカプセル化や一般化の原則の実現体であるように、\nクラスのインタフェースもデータカプセル化(data encapsulation)の原則を実\n現したものにすべきだ。\n13.8 節で議論したマルコフ解析はよい実例を提供してくれる。\nhttp://thinpython.com/code/markov.py をダウンロートしてそのコードを眺め\nると、いくつかの関数から読み書きされる二つの大域変数suffix map とprefix\nとが定義されている:\nsuffix_map = {}\nprefix = ()\nこれらの変数は大域変数であるので、一度に一つのテキストの解析ができるだけ\nである。もし二つのテキストを同時に解析するとなると、同じデータ構造に追加\nすることになる(これ自体は興味のある結果を生むが)\n。複数の解析を別々なデー\n'}, {'id': '54aeba13-afde-4aff-a8e9-37b920a91349', 'page': 250, 'text': '250\n第18 章\n継承\nタ構造に収めようとすると、各解析の状態を一つのオブジェクトにカプセル化す\nる必要がある。それは以下のようになる:\nclass Markov(object):\ndef __init__(self):\nself.sufix_map = {}\nself.prefix = ()\n次ぎに関数をメソッドに変換する。例として、process word 関数を変換してみる:\ndef process_word(self, word, order=2):\nif len(self.prefix) < order):\nself.prefix += (word,)\nreturn\ntry:\nself.suffix_map[self.prefix].apppend(word)\nexcept KeyError:\n# if there is no entry for this prefix, make one\nself.suffix_map[self.prefix] = [word]\nself.prefix = shift(self.prefix, word)\n関数の中味を変えずにデザインを変える手法はプログラムの変更方法に対する再\n因子分解のもう一つの事例である(4.7 節)\n。\nこの例はオブジェクトやメソッドをデザインするための開発計画を示唆している\n:\n1. まず、大域変数(もし必要なら)を使って読み・書きする関数を書く。\n2. そのプログラムが動くようになったら、それらの大域変数とそれらを使う関\n数の関係を探す。\n3. 関連する変数を一つのオブジェクトの属性としてカプセル化する。\n4. 付随する関数は新たなクラスのメソッドになるように変換する。\n練習問題18.5 13.8 節のサンプルコードを\nhttp://thinkpython.com/code/markov.py からダウンロードし、上記のステッ\nプに従い大域変数を新たなクラスMarkov の属性としてカプセル化せよ。\n解答例:http://thinkpython.com/code/Markov.py (大文字のM に注目)\n。\n'}, {'id': 'f6092322-1250-467c-a26e-1e91fcbd4636', 'page': 251, 'text': '18.11.\n語句\n251\n18.11\n語句\n符号化(encode)\n:二つの集合の対応関係を構築することによって一つ値の集合\nを他の値の集合で表現する。\nクラス属性(class attributes)\n:クラスオブジェクトに属する属性。クラス属性\nはクラスの定義の中で定義されるが如何なるメソッドの外側に置く。\nインスタンス属性(instance attributes)\n:クラスのインスタンスに属する属性。\nベニヤ(veneer)\n:大した計算過程も無く他の関数に異なったインターフェース\nを提供するメソッドや関数。\n継承(inheritance)\n:既存のクラスの改訂版になるような新しいクラスを定義す\nる能力。\n親クラス(parent class)\n:それから子クラスが派生する元のクラス。\n子クラス(child class)\n:既存のクラスから派生して作られた新しいクラス。\n「下\n位クラス」とも呼ばれる。\nIS-A 関係(IS A relationship)\n:子クラスとその親クラスといった関係。\nHAS-A 関係(HAS A \u3000relationship)\n:二つのクラスで一つクラスのインス\nタンスが他のクラスのインスタンスへの参照を含むような関係。\nクラス図(class diagram)\n:一つのプログラムの中に現れるクラスとそれらの間\nの関係を示すグラフィカルな表現。\n重複度(multiplicity)\n:HAS-A 関係の関係にある二つのクラスで他のクラスの\nのインスタンスへの参照が何回あるかを示す表示法。\n18.12\n練習問題\n練習問題18.6 以下はポーカーの可能な手を値打ちの少ない順序(従って確率の\n大きな順序)に並べたものである:\npair: 同じランクを持った二枚のカード\ntwo pair: 同じランクを持った二枚のカードの二組\nthree of a kind: 同じランクを持った三枚のカード\n'}, {'id': '0c1917c0-3e07-464c-93a6-f95b94874a73', 'page': 252, 'text': '252\n第18 章\n継承\nstraight: 引き続くランクからなる五枚のカード\n(エースは高くも低くにもなれる。\n従って、Ace-2-3-4-5 はstraight、10-Jack-Queen-King-Ace もstraight、しか\nし、Queen-King-Ace-2-3 はstraight ではない)\nflush: 同じスートの五枚のカード\nfull house: 一つのランクの三枚のカードと他のランクの二枚のカード\nfour of a kind: 同じランクの四枚のカード\nstraight flush: 同じスートで引き続くランク(上で定義したように)からなる五\n枚のカード\nさてこの練習問題の目標は上のような手を引き当てる確率を推定することにある。\n1. 以下のファイルをhttp://thinkpython.com/code からダウンロードせよ。\nCard.py :これはCard、Deck、Hand のクラス記述の完全版である。\nPokerHand.py :ポーカーの手を表現するためのクラスの不完全な記述とそ\nのテストコードである。\n2. PokerHand.py を起動すると“7-card”ポーカーの七枚の手札が配られ、この\n手札にflush が含まれているかをチェ\nックするようになっている。\n3. PokerHand にhas pair、has twopair というメソッドを追加せよ。これらの\nメソッドは手札に当該の手が含まれているとTrue を、そうでなければFalse\nを返す。コードは任意の枚数の手札でも手を検出できること(5 とか7 とか\nがよくある数であるが)\n。\n4. 一つの手札に含まれる最高位の手を調べるclassify メソッドを作成せよ。\nそして属性のlabel をそれに従ってセットせよ。例えば、“7-card”の手とし\nてflush を含んでいたら、’flush’ という文字列をlabel に代入する。\n5. このclassify メソッドが動くことが確認できたら、次ぎのステップは種々\nの手の確率を求める番だ。PokerHand.py 内に積み札をシャ\nッフルしてそれ\nを手札に分割し、各手札をclassify で分類し、特定の分類値が起こる回数\nを調べる関数を書け。\n6. 分類値とその確率を表にしてプリントせよ。充分長く実行して沢山の手を\n発生させその確率が適当な桁で落ち着くまでプログラムを実行せよ。結果を\nhttp://en.wikipedia.org/wiki/Hand_rankinge と比較せよ。\n'}, {'id': 'a1735604-22b9-44e1-b23c-90df694a9422', 'page': 253, 'text': '18.12.\n練習問題\n253\n練習問題18.7 この練習問題では第四章で扱ったTurtleWorld を使う。\nこのTurtle でゲームtag(鬼ごっこ)が遊べるプログラムを作成する。ゲーム\ntag に馴染みのない読者はhttp://en.wikipedia.org/wiki/Tag.(game) を参照\nせよ。\n1. http://thinkpython,com/code/Wobbler.py をダウンロードし、起動して\nrun ボタンを押すと三匹のカメが画面上を乱雑に動き回ることを確認せよ。\n2. そのコードを眺め、動きを確認せよ。Wobbler クラスはTurtle クラスを継\n承している。つまり、lt、rt、fd、bk メソッドはWobbler でも有効だ。step\nメソッドは個々のぶらつきカメ(Wobbler)に対してTurtleWorld によって\n発動される。またstep メソッドはある決められた方向にカメを向けるsteer\nメソッドを発動、カメの「不器用さ」に比例して乱雑な方向転回を繰り返す\nwobble メソッドを発動、カメの「スピード」に比例して前進するmove メソッ\nドを発動する。\n3. ファイルTagger.py を作成せよ。Wobbler.py の全てをインポート、Wobbler\nを継承したクラスTagger を作成せよ。Tagger クラスオブジェクトを引数\nに渡してmake world を呼び出せ(訳注:これで世界の主役は鬼ごっこカメ\n(tagger)たちになる)\n。\n4. Wobbler クラスのsteer メソッドを再定義することになるTagger クラスの\nsteer メソッドを作成せよ。簡単のためここでは全てのカメを原点の方向に\n向ける。ヒント:Turtle の属性x、y、heading と数学関数atan2 を使え。\n5. 全てのカメは境界(-200,200)内にいるようにsteer メソッドを修正せよ。\nデバッギングのために全てのカメの動きを一歩ずつ進めるStep ボタンで動\n作を確認することも良いかもしれない。\n6. 最も近い相手の方向を向くようにsteer を修正せよ。ヒント:カメは自分\nたちが住んでいる世界TurtleWorld についての属性world を持っている。\nこのTurtleWorld の属性としてその世界に住む全てのカメをリストにした\nanimals を持っている。\n7. ゲームtag が演じられるようにsteer を修正せよ。Tagger クラスに新規のメ\nソッドを追加することは構わない。またsteer と__init__を再定義できる\nが、step、wobble、move の修正や再定義はしてはいけない。また、steer メ\nソッドではカメの頭の方向を制御するのみで位置の制御はできないとする。\n解答例:http://thinkpython.com/code/Tagger.py\n'}, {'id': 'b59bc6a7-fb42-4785-a42b-cc47e901dd28', 'page': 1, 'text': ''}, {'id': 'a70bc2b4-1ca2-4f39-9a41-7c1b3e6a9cf3', 'page': 255, 'text': '255第19章事例研究:Tkinter19.1GUIこれまでの大多数がテキストベースのプログラムであったが、多くのプログラムはグラフィックユーザインタフェース(graphicuserinterface)つまりGUIと呼ばれているものを使っている。Pythonは、wxPyhton、Tkinter、Qtなどを含め、GUIベースのプログラムを作成するための仕掛けをいくつか提供している。各々は一長一短があり、Pythonではどれを標準とするまでには至っていない。この章では初心者に最も馴染みやすいと考えてTkinterを取りあげる。ここで紹介する概念は他のGUIモジュールにも適用できるものである。Tkinterについては沢山の書籍やWebページがあるが、オンラインで使える最良の資料はFredrikLundh著“AnIntroductiontoTkinter”だ。モジュールGUI.pyがパッケージSwampyに同梱されている。これはTkinterの関数やメソッドにたいする簡単なインタフェースを提供している。この章のサンプルはこのモジュールを使っている。一つのGUIを生成するためには、Guiモジュールをインポートし、一つのGuiオブジェクトを具現化しなければならない:fromGuiimport*g=Gui()g.title(’Gui’)g.mainloop()このプログラムを起動すると何もない灰色の正方形で、タイトルにGuiが付いた窓が出るはずである。mainloopはイベント・ループ(eventloop)であり、ユーザがすることを監視し、それに対応するために待機している。これは無限ループであり、ユーザが窓を閉じる、Control-Cボタンを押す、またはユーザがプログラムを終了にもたらす何かをするまで続く。このGuiはウィジェット(部品)(widgets)を一つも持たないので大したことはできない。ウィジェットはGuiを構成する要素である。どんなものがあるか列記する:\x0c'}, {'id': '8ba64327-868f-4511-8171-a55a6b83d617', 'page': 256, 'text': '256第19章事例研究:Tkinterボタン(Button):それが押されると何かのアクションが実行される文字また画像でできたウィジェットである。カンバス(Canvas):線、長方形、円や他の図形を描くことができる領域である。エントリ(Entry):ユーザがテキストを入力できる領域である。スクロールバー(Scrollbar):他のウィジェットの可視化領域を制御するウィジェットである。フレーム(frame):大抵は見えないが、他のウィジェットを含んだ入れ物である。一つのGuiを生成したときに現れた灰色の正方形がフレームである。新たにウィジェットを生成するとそれはこのフレームに追加される。19.2ボタンとコールバックメソッドbuでボタンが生成される:button=g.bu(text=’Pressme.’)メソッドbuからの戻り値はボタンオブジェクトである。フレーム上に現れたこのボタンはこのオブジェクトのグラフィックスを用いた表現である。このオブジェクトに対して発動されるメソッドによってこのボタンを制御できる。メソッドbuは見栄えや関数の制御のために32個の引数を受け取る。それらの仮引数はオプション(options)と呼ばれている。32個のオプションの全てに値を入れる替わりにtext=’Pressme.’のようなキーワード付き引数機能(keywordargument)を使って必要なオプションだけを設定できる。ところでフレームにウィジェットを追加すると、それは「密着包装」される、つまり、フレームはウィジェットの大きさになる。さらにウィジェットを追加するとフレームはそれらを包容するように大きくなる。\u3000メソッドlaはラベルを生成する:label=g.la(text=’Pressthebutton.’)既定値としてTkinterはウィジェットを上から下へ左右の中心に置く。この振る舞いを再定義する方法には直ぐに出会う。ボタンを押しても何も起こらない。なぜならばまだ「配線がされて」いないからだ、つまり、何をすべきが教えてないからだ。ボタンの振る舞いを制御するオプションはcommandである。このcommandが持つ値はそのボタンが押されたときに実行される関数である。その関数の例を以下に示す。\x0c'}, {'id': '3bbe5af3-aa91-437e-a1a7-7aefe99437d7', 'page': 257, 'text': '19.3.\nカンバス\n257\ndef make_label():\ng.la(’Thank you.’)\nこの関数をcommand としたボタンを作ってみよう:\nbutton2 = g.bu(text=’No, press me!’, command=make_label)\nボタンが押されたときmake label が実行され新たなラベルが出現する。command\nオプションの値は関数オブジェクトであり、ボタンを生成するためにbu を読んだ\n後に、ユーザはボタンを押したときに実行の流れが「呼び戻される」のでコール\nバック(call back)と呼ばれている。\nこのような実行の流れはイベント駆動型プログラミング(event-driven pro-\ngramming)の特徴である。ユーザがボタンを押す、キーを叩くなどはイベント\n(events)と呼ばれる。イベント駆動型プログラミングでは、実行の流れはプロ\nグラマーではなく、ユーザの行動によって決められる。イベント駆動型プログラ\nミングの挑戦的なところはウィジェ\nットと、ユーザのあらゆる行動に対して正確に\n対処できる(少なくとも適当なエラーメッセージを吐き出す)コールバック関数\nの集合を構築することにある。\n練習問題19.1 一つの単純なボタンを持つGUI を生成するプログラムを書け。こ\nのボタンを押すと第二のボタンが現れ、これを押すと’Nice job!’ と表示するラ\nベルが作られるようにせよ。ボタンを何回も押すと何が起きるかな?\n解答例:http://thinkpython.com/code/button_demo.py\n19.3\nカンバス\n最も多芸なウィジェ\nットの一つが線、円や他の図形を描くことができる領域を提\n供するカンバスである。練習問題15.4 をやった読者はカンバスには馴染みがある\nだろう。\n新たにカンバスを生成するメソッドは以下のようである:\ncanvas = g.ca(width=500, height=500)\nwidth とheight はピクセル単位で指定するカンバスのサイズだ。ウィジェ\nットを\n生成した後でもconfig メソッドを使ってオプションの値は変更できる。例えば、\n背景色を指定するbg オプションを使ってみる:\ncanvas.config(bg=’white’)\n'}, {'id': 'f862d558-1b28-40d7-b1eb-5cfde5f970be', 'page': 258, 'text': '258第19章事例研究:Tkinterbgの値は色を表す文字列である。可能な色の名前はPythonの実装によって異なるが、以下の色名は共通している:whiteblackredgreenbluecyanyellowmagentaカンバス上の図形はアイテム(item)と言う。例えば、円を描きたいとしたら以下のようにする:item=canvas.circle([0,0],100,fill=’red’)第一引数は円の中心を指示する座標の値の対であり、第二引数は半径である。Gui.pyはカンバスの中心を座標原点とする二次元平面で、上がy軸の増加方向になる座標系を提供している。これはよくあるグラフィックス系と異なる。オプションfillは円を赤で埋めることを意味している。circleからの戻り値はこのカンバス上のウィジェットの特性を変更できるようにするアイテムオブジェクトである。これを使って円の特性を変更してみよう:item.config(fill=’yellow’,outline=’orange’,width=10)widthは輪郭線の幅、outlineはその色である。練習問題19.2一つのカンバスと一つボタンを生成するプログラムを作成、ボタンを押すとカンバスに円を描くようにせよ。19.4座標の配列長方形を描くときには対角線上の頂点の座標の値を配列で渡す。例えば、左下の頂点は座標原点で、右上の座標は(200,100)である青の長方形を描くとする:canvas.rectangle([[0,0],[200,100]],fill=’blue’,outline=’orange’,width=10)このような頂点の座標を指定する方法は、この二つの点で長方形が拘束されるので境界ボックス(boundingbox)法という。ovalは境界ボックスを引数に取り指示された長方形に従って楕円形を描く:canvas.oval([[0,0],[200,100]],outline=’orange’,width=10)lineは座標の配列を受け取り、それらを結ぶ線を描く。例は三角形の二辺である:\x0c'}, {'id': '544c38b8-ad99-40ef-9e6f-1c6878c73cd8', 'page': 259, 'text': '19.5.さらなるウィジェット259canvas.line([[0,100],[100,200],[200,100]],width=10)polygonは同じ引数だが、必要に応じて多角形最後の辺も描き、閉じた図形にする:canvas.polygon([[0,100],[100,200],[200,100]],fill=’red’,outline=’orange’,width=10)19.5さらなるウィジェットTkinterはユーザの文字入力の方法を二つ提供している。一つは一行入力のエントリであり、他は複数行のテキストの入力が可能なテキストボックスである。enは新規のエントリを生成する:entry=g.en(text=’Defaulttext.’)オプションtextでこのエントリが生成されたときに表示する文字列を指示できる。メソッドgetでエントリの中味を(多分ユーザによって変えられた)を得ることができる。>>>entry,get()’Defaulttext.’teはテキストボックスを生成する:text=g.te(width=100,height=5)widthとheightは文字数と行数で指示されるボックスの大きさである。insertメソッドでボックスのテキストを挿入できる:text.insert(END,’Alineoftext.’)ENDは特殊なインデックスでテキストボックスの最後の文字を意味する。さらにドット表記(1.1のような、ドットの前の数で行数、後で桁数を示す)で挿入位置を指定することもできる。以下の例では一行目の最初の文字の後に’nother’を挿入することになる:>>>text.insert(1.1,!nother’)getメソッドはテキストボックスから文字列を取り出す。引数として最初から最後までのインデックスを与えてあるので、改行記号も含めて取り出せる:\x0c'}, {'id': 'de2bc23e-c7fd-480c-af30-20467f33d2b4', 'page': 260, 'text': '260第19章事例研究:Tkinter>>>text.get(0.0,END)’Anotherlineoftext.\\n’deleteメソッドはテキストボックス内の文字を消去する。以下の例は先頭の二文字を残して残りを全部削除する:>>>text.delete(1.2,END)>>>text.get(0.0,END)’An\\n’練習問題19.3練習問題19.2を修正して一つのエントリと第二のボタンを追加、第二のボタンを押すとエントリに与えた色名を得て円の流し込みの色を変更するようにせよ。そのためにはconfigメソッドを使う。プログラムはユーザが円の生成しない前にこのボタンを押す、間違った色名を入力するといった状況にも対処すること。解答例:http://thinkpython.com/code/circle_demo.py19.6パッキングウィジェットこれまでのウィジェットは一段に積み重ねて置かれた。しかし、多くのGUIではレイアウトはもっと複雑だ。例えば、図19.1には簡単化されたTurtleWorldを示した。この章ではこのGUIをいくつかのステップに分けて作るコードを紹介する。全体のコードはhttp://thinkpython.com/code/SimpleTurtleWorld.pyからダウンロードできる。このGUIの最上部には一つのカンバス、一つのフレームが含まれる。これらは一行に揃えて置かれる。したがって、第一のステップはこの行を生成することである:classSimpleTurtleWorld(TurtleWorld):"""ThisclassisidenticaltoTurtleWorld,butthecodethatlaysouttheGUIissimplifiedforexplanatorypurposes."""defsetup(self):self.row()........\x0c'}, {'id': 'cac69155-a78f-4361-9b58-7c76d51c4e35', 'page': 261, 'text': '19.6.\nパッキングウィジェ\nット\n261\n図19.1: TurtleWorld:snowflake コードが終了した時点。\nsetup 関数はウィジェ\nットの生成とアレンジをする関数である。GUI にウィジェ\nッ\nトをアレンジすることはパッキング(packing)と呼ばれる。row メソッドは新規\nのフレームを生成し、それを「カレント・フレーム」とする。このフレームが閉\nじられるか新しいフレームが生成されるまで、引き続き生成されるウィジェ\nットは\nこの行フレームに付けられる。\n以下のコードで一つカンバスと一つの列フレームが生成される。\nself.canvas = self.ca(width=400, height=400, bg=’white’)\nself.col()\nこの列フレームの第一ウィジェ\nットはグリッドフレームで2x2 の四つボタンを付\nける:\nself.gr(cols=2)\nself.bu(text=’Print canvas’, command=self.canvas.dump)\nself.bu(text=’Quit’, command=self.quit)\nself.bu(text=’Make Turtle’, command=self.make_turtle)\nself.bu(text=’Clear’, command=self.clear)\nself.endgr()\ngr は引数に列数を受け取り、グリッドフレームを生成する。グリッド上のウィジェ\nッ\nトの配置は左から右へ、上から下への順である。\n'}, {'id': '55329276-6f35-47f5-93bb-30e2857719ec', 'page': 262, 'text': '262\n第19 章\n事例研究:Tkinter\n第一番目のボタンはself.canvas.dumpをコールバックとして使い、\n第二はself.\nquit を使う。これらは特別なオブジェクト(インスタンス)に付随したものであ\nるので結合メソッド(bound method)と呼ばれる。それらが発動されるときは、\nそのオブジェクトに対して発動される。\n列フレームの次ぎのウィジェ\nットは行フレームである:それは一つのボタンと一\nつのエントリからなる:\nself.row([0,1], pady=30)\nself.bu(text=’Run file’, command=self.run_file)\nself.en_file = self.en(text=’snowflake.py’, width=0)\nself.endrow()\nrow メソッドに対する第一の引数は重みのリストであり、ウィジェ\nットの間に許さ\nれる余分なスペースの許容程度を示す。リスト[0,1] は第二のウィジェ\nット(エン\nトリ)にはいくらでも余分なスペースが与えられる。このコードを実行し、窓の\n大きさを変えてみると、エントリは大きくなるが、ボタンはそうでないことが分\nかる。オプションpady は行フレームのy 方向、つまり上下に30 ピクセルの「詰\nめ物」をすることである。endrow メソッドはこの行フレームの終了を意味する。\n従って次ぎからのウィジェ\nットは上位の列フレームに積み重さなることになる。\nこのようにGui.py はフレームのスタックを追跡・管理している。\n• row、col、gr フレームのどれかを生成するとそれがスタックの最上位にな\nり、カレントフレームとなる。\n• endrow、endcol、endgr が実行されると対応するフレームは閉じられ、フ\nレームスタックから削除され、スタックの最上位になぅたフレームがカレン\nトフレームとなる。\nメソッドrun file はエントリの中味を読み、それをファイル名としてそのファイ\nルの中味を読みrun code に渡す。self.inter はインタプリタ・オブジェクトで、\n渡された文字列を読みそれをPython コードとして実行する。\ndef run_file(self):\nfilename = self.en_file.get()\nfp = open(filename)\nsource =fp.read()\nself.inter.run_code(source, filename)\n最後の二つのウィジェ\nットはテキストボックスとボタンである:\n'}, {'id': 'abf28498-7986-402a-a46b-cd0155e4752d', 'page': 263, 'text': '19.7.\nメニューとコーラブル\n263\nself.te_code = self.te(width=25, height=10)\nself.te_code.insert(END, ’world.clear()\\n’)\nself.te_code.insert(END, ’bob = Turtle(world)\\n’)\nself.bu(text=’Run code’, command=self.run_text)\nメソッドrun text はrun file と同じような働きをする。しかし、今回はテキス\nトボックスにあるテキストをコードとして実行する:\ndef run_text(self):\nsource = self.te_code.get(1.0, END)\nself.inter.run_code(source, ’’)\n不幸にして、ウィジェ\nットのレイアウトの細部は他の言語やPython モジュール\n間で異なっている。Tkinter だけとっても三種類のウィジェ\nット配置機構を持って\nいる。これらの機構は幾何学的配置管理(geometry managers)と呼ばれる。こ\nの節で私が紹介した方法は「グリッド型」幾何学的配置管理であり、他には、\n「パッ\nク型」と「プレイス型」がある。\n幸にして、この節で紹介した概念は他のGUI モジュールや他の言語でも通用\nする。\n19.7\nメニューとコーラブル\nメニューボタンは普通のボタンのように見えるが、それを押すとメニューが飛\nび出す。ユーザがアイテムを選択するとそのメニューは消える。\ng = Gui()\ng.la(’Select a color’)\ncolors = [’red’, ’green’, ’blue’]\nmb = g.mb(text=colors[0])\nmb でメニューボタンが作られる。ボタンの上のテキストは初期状態では既定値の\n色名である。\n以下のループは個々の色に対するメニューを生成する。\nfor color in colors:\ng.mi(mb, text=color, command=Callable(set_color, color))\n'}, {'id': '5c9d66d0-ad03-43dd-b4cd-9cd361022723', 'page': 264, 'text': '264\n第19 章\n事例研究:Tkinter\nmi メソッドの第一引数はこれらのアイテムが所属するメニューボタンである。\ncommand オプションは「コーラブル」オブジェクトで新しい内容だ。これまで\nコールバック関数として、関数や結合メソッドを使ったが、これらは引数が何も\nないときには有効であった。そうでない場合は関数名(set color のような)と引\n数(color のような)を共に引数として渡すコーラブル(Callable)オブジェク\nトを生成する必要がある。コーラブルオブジェクトは関数への参照と引数を属性\nとしている。ユーザがメニューの当該のアイテムをクリックすると、このコール\nバック関数はその関数をそれに引数を渡して実行する。\nset color 関数は以下のような感じである:\ndef set_color(color):\nmb.config(text=color)\nprint color\nユーザはメニュー上のそのアイテムを選択すると、set color 関数が呼ばれ、メ\nニューのボタンのテキストが選択された色名で更新され、且つprint される。こ\nれによってset color の呼び出しはアイテムが選択されたときであることも分か\nる(そしてこのコーラブルオブジェクトが生成されたときではないことも了解で\nきる)\n。\n19.8\nバインディング\nバインディング(binding)はウィジェ\nット、イベント、そしてコールバックの\n間の纏まりである。あるイベント(ボタンが押されたとか)があるウィジェ\nット上\nで発生するとコールバックが発動される。\n多くのウィジェ\nットは既定のバインディングを持っている。例えば、ボタンが押\nされるとボタンが押されたような外観を作るために既定のバインディングはボタ\nンの開放へと変わる。ボタンが開放されると、ボタンの外観は元に戻り、command\nオプションで指定されたコールバックが発動される。\nバインディングメソッドを使ってこの既定のバインディングを再定義したり、新\nたにバインディングを生成したりできる。以下はカンバスに対する一つのバイン\nディングの例である。\nca.bind(’’, make_circle)\n第一の引数はイベント文字列である。このイベントはユーザがマウスの左ボタンを\n押したときに発生する。\n他のマウスイベントには、\nButtonMotion、\nButtonRelease、\nDouble-Button がある。第二の引数はイベントハンドラである。イベントハンド\n'}, {'id': 'ff81879d-14e3-4935-b077-7b163d6a239d', 'page': 265, 'text': '19.8.\nバインディング\n265\nラはコールバックのような関数または結合メソッドである。コールバックと大き\nく異なる点は、イベントハンドラは引数としてイベントオブジェクトを取ること\nである。例を上げる。\ndef make_circle(event):\npos = ca.canvas_coords([event.x, event.y])\nitem = canvas.circle(pos, 5, fill=’red’)\nイベントオブジェクトはイベントのタイプやマウスが指している座標の情報など\nの詳細な情報を含んでいる。この例では、必要なものはマウスがクリックされた\n画面上の位置である。その値はそのグラフィ\nックス系で定義されている「ピクセ\nル座標」である。それをcircle メソッドが使う「カンバス座標」に変換するのが\ncanvas coords である。\nエントリではユーザが改行やエンターキーを叩いたときに発生するイ\nベントをバインドするのが一般的だ。以下は一つのボタンと一つのエントリを生\n成するコードである:\nbu = g.bu(’Make text item:’, make_text)\nen = g.en()\nen.bind(’’, make_text)\nコールバック関数make text はボタンが押されたときかユーザがエントリで改行\nキーを入力したとき発動される。従ってmake text はオプションcommand の値と\nした関数(引数なし)とイベントハンドラとした関数(引数はイベントオブジェ\nクト)の双方を満たす関数である。\ndef make_text(event=None):\ntext = en.get()\nitem = ca.text([0,0], text)\n関数make text はエントリの中味を受け取り、カンバスの中心にそのテキストを\n表示する。\nカンバス上にあるアイテムに対してバインドを生成することもできる。以下は\nDraggable クラスの例である。これはドラック-アンド-ドロップ機能を実行するバ\nインドをItem の子クラスとして設計する。\nclass Draggable(Item):\ndef __init__(self, item):\nself.canvas = item.canvas\nself.tag = item.tag\n'}, {'id': 'e7d4d61a-1aec-4b10-9411-271a38890aa6', 'page': 266, 'text': '266\n第19 章\n事例研究:Tkinter\nself.bind(’’, self.select)\nself.bind(’’, self.drag)\nself.bind(’’, self.drop) \u3000\ninit メソッドは一つのitem を引数として受け取る。このitem の二つの属性のコ\nピーを作り、ボタンが押された、ボタンが動く、ボタンが放されたという三つの\nイベントを生成する。\nイベントハンドラselect は当該のイベントが発生した座標と、このアイテムの\n色名を保存し、アイテムの色を黄色に変える。\ndef select(self, event):\nself.dragx = event.x\nself.dragy = event.y\nself.fill = self.cget(’fill’)\nself.config(fill=’yellow’)\ncget は「構成を得よ」の略である。アイテムのオプションの情報を得るメソッド\nで、今は流し込みの色名が戻り値で得られる。drag メソッドは出発点からオブジェ\nクトがどのように動いたかを計算し、出発点を更新し、それに合わせてアイテム\nを移動させる。\ndef drag(self, event):\ndx = event.x - self.dragx\ndy = event.y - self.dragy\nself.dragx = event.x\nself.dragy = event.y\nself.move(dx, dy)\nこの計算は「ピクセル座標」でなされ、\n「カンバス座標」に変換する必要はない。\n最後のdrop メソッドはアイテムの色を元の色に戻す。\ndef drop(self, event):\nself.config(fill=self.fill)\nこのDraggable クラスを使って、カンバス上の任意のアイテムにドラック-アン\nド-ドロップ機能を追加することができる。以下はmake circle にこの機能を追加\nしたものだ:\n'}, {'id': 'cb121163-9972-4eaf-bf37-587fcc001eba', 'page': 267, 'text': '19.9.\nデバッギング\n267\ndef make_circle(event):\npos = ca.canvas_coord([event.x, event.y])\nitem = canvas.circle(pos, 5, fill=’red’)\nitem = Draggable(item)\nこの例は継承の利点を示すもう一つの模範例だ。その定義を変更することをし\nないで親クラスが持つ機能を変更できるのだ。自分が作成したものでないモジュー\nルに対してその振る舞いを変更したいときに極めて有用な方法だ。\n19.9\nデバッギング\nGUI プログラミングで挑戦的なことはGUI を構築中に何が起こり、その後ユー\nザが介在するイベントに対応して何が起こるかをキチンと把握して置くことだ。例\nえば、コールバック関数の設定で、よくある間違いは関数への参照の替わりに関\n数の呼び出しをしてしまうことだ:\ndef the_callback():\nprint ’Called.’\ng.bu(text=’This is wrong!’, command=the_callback())\nこのコードを実行すると、関数the callabck は直ちに実行され、次ぎにボタンは\n生成されることになる。さらに、この関数の戻り値がNone だから、ボタンを押し\nても何も起こらない。普通はGUI を設定しているときにはコールバック関数の発動\nは起こってほしくない。ユーザのイベントの応答に対して発動して欲しいわけだ。\nもう一つの挑戦的なことは、実行の流れを制御するのがプログラマーでないこ\nとだ。プログラムのどの部分が、どうような順序で実行されるかはユーザの行動\nによって決められるわけだ。このことは、プログラムは起こり得るどんなイベン\nトに対しても正確の動くようにプログラムを設計する必要があることを意味して\nいる。ウィジェ\nットの数が増えてくると、起こり得るイベントの系列の全てを想像\nすることは益々困難になる。このような複雑な状況を管理する一つの方法はシス\nテム全体の状況をある特定のオブジェクトに押し込めてしまい、それから考察す\nることだ。\n• システム全体の取り得る状態は何か\n?\n練習問題19.2の円を描く問題では、\nユー\nザが第一の円を生成した前か後か、これがシステム全体が取り得る状態であ\nると考える。\n'}, {'id': '1968c3e3-e433-47ee-adf5-4aea9567ffa9', 'page': 268, 'text': '268第19章事例研究:Tkinter•この二つの状態の各状態に対して、どのようなイベントが起こり得るか?今の例でいうと、二つあるボタンのどちらかを押すか、プログラムを終了するかだ。•システムの二つの状態の一つと起こり得るイベントの一つ、このペアが起きたとき欲しい結果は何か?状態数は二つ、ボタンは二つなので、考え得るペアの数は4となる。•システムの状態がある状態から別の状態へ遷移する原因になっているものはなにか?今の例でいうと、ユーザが第一の円を描画したときその遷移が起きるわけである。また、イベントの系列に対して保存されるべき不変性を定義し、それが保たれているかチェックすることも有益だ。GUIプログラミングに対するこのアプローチは全ての起こり得るユーザイベントの系列を検証することに時間を費やすことなく、正しいプログラムを作成する手助けになるはずだ。19.10語句グラフィックユーザインタフェース(GUI):グラフィカルなアイテムを使ったユーザインタフェースウィジェット(部品)(widgets):ボタン、メニュ、テキスト入力窓等を含むGUIを構築するための部品。オプション(options):ウィジェットの見かけや機能を制御するための値。キーワード付き引数(keywordargument):関数呼び出し際に仮引数名を明示した実引数。コールバック(callback):ウィジェットに対してユーザが起こした動作を受けて呼び出される関数。結合メソッド(boundmethod):特別なインスタンスに付随しているメソッド。.イベント駆動型プログラミング(event-drivenprogramming):プログラムの実行の流れがユーザの動作によって決められるようなプログラミングスタイル。イベント(events):GUIに対応をもたらすマウスのクリックやキーが押されたといったユーザの動作。\x0c'}, {'id': 'b0fdb0d2-3e6f-442e-a827-4a71108fbf8c', 'page': 269, 'text': '19.11.練習問題269イベント・ループ(eventloop):ユーザの動作や応答を待っている無制限のループ。アイテム(item):カンバスウィジェット上にあるグラフィカルな部品。境界ボックス(boundingbox):様々なアイテムを取り囲む長方形。通常対角線上にある頂点の座標で指定される。パッキング(packing):GUIで様々な部品を整理し表示すること。幾何学的配置管理(geometrymanagers):ウィジェット類を体系的にパッキングするシステム。バインディング(binding):ウィジェット、イベントそしてイベント処理関数の集まりを束ねる。19.11練習問題練習問題19.4この練習問題はイメージビュアを作ることである。以下に簡単な例を示す:fromswampy.Guiimport*fromTkinterimportPhotoImageg=Gui()canvas=g.ca(width=300)photo=PhotoImage(file=’danger.gif’)canvas.image([0,0],image=photo)g.mainloop()PhotoImageはファイルを読んでTkinterで表示可能なPhotoImageオブジェクトを返す。canvas.imageは与えられた座標を中心として画像をカンバスに貼り付ける。この画像はラベル、ボタンや他のいくつかのウィジェットに貼り付けることができる:g.la(image=photo)g.bu(image=photo)\x0c'}, {'id': 'ea37ac68-e710-4f77-9794-cd95c5c757b2', 'page': 270, 'text': '270\n第19 章\n事例研究:Tkinter\nPhotoImage モジュールはGIF とかPPM とかの数少ない画像形式にのみ対応してい\nるが、Python Image Library(PIL) を使うと多くの画像形式に対応できる。そのモ\nジュール名はImage で、Tkinter も同じ名前のモジュールを使っている。衝突を\n避けるためにimport..as 形式を使ってインポートする:\nimport Image as PIL\nimport ImageTK\n第一行はPIL のImage モジュールをPIL という名前にしてインポートした。第二\n行目はPIL のImageTk モジュール(これはPIL 形式のPhotoImage をTkinter 形\n式のPhotoImage に変換する)をインポートした。画像表示の例を示す:\nimage = PIL.open(’allen.png’)\nphoto2 = ImageTk.PhotoImage(image)\ng.la(image=photo2)\n以上を以下の手順で行え:\n1. image demo.py、danger.gif、allen.png をhttp://thinkpython/code か\nらダウンロードせよ。その上で、image demo.py を実行せよ。PIL や(PIL\nの一つのモジュールである)ImageTk をインストールしなければならないか\nもしれない。\n2. image demo.py の中で第二画像の代入をphoto2 からphoto に変え、プログ\nラムを実行せよ。第二の画像だけが表示され、第一の画像は表示されないは\nずだ。この事情はphoto に第二のPhotoImage 関数の戻り値を代入すると、\n第一のPhotoImage 関数の戻り値に対する参照を上書きしてしまうことから\n起きる。同じことがPhotoImage 関数の戻り値を局所変数に代入したときも\n関数が終了すると画像は消えてしまう。これを避けるには表示を続けたい\nPhotoImage にたいする参照をデータ構造に保存するか、オブジェクトの属\n性にしてしまうことだ。この振る舞いは困ったものだ、“Danger!”という画\n像を表示させたのも私の警告のつもりだ。\n3. このプログラムを元にして、あるディレクトリ内にあるPIL が画像として認\n識できる全ての画像を表示するプログラムを作成せよ。例外処理try を利用\nして、PIL が認識できないファイルを処理することも考えよ。画像をクリッ\nクすると、次の画像が表示されるようにもせよ。\n4. PIL は多くの画像処理のメソッドを提供している。それについては\nhttp://pythonware.com/pil/handbook を参照せよ。それらのいくつかを\n使って画像の編集を可能にするようにGUI を構築せよ。\n'}, {'id': '3970f39a-c1bb-4f89-921a-df6f7485cef0', 'page': 271, 'text': '19.11.練習問題271解答例:http://thinkpython.com/code/ImageBrowser.py練習問題19.5ベクターグラフィックスエディタとはユーザが種々の幾何学図形(線、円、長方形など)を画面上に描画・編集できるプログラムのことである。このプログラムはPostscriptやSVGのようなグラフィク形式の出力ファイルを生成できる機能を持っている。Tkinterを使って簡単なグラフィックスエディタを作成せよ。少なくともユーザが線、円、長方形をスクリーン上に描画できること、Canvas.dumpメソッドを使ってカンバス上の内容をPostscriptで記述しファイルとして出力できることを実現せよ。挑戦として、ユーザがカンバス上のアイテムを選択、そのアイテムの大きさを(マウス操作で)変えられるようにせよ。練習問題19.6Tkinterを使って基本的な機能を持つWebブラウダーを作成せよ。検索したいURLを入力する1つのテキストボックスとそのページの内容を表示するカンバスを持つようにする。ファイルのダウンロードにはモジュールurllib(練習問題14.6)が使える。モジュールHTMLParserでHTMLタグの解析ができる(詳細はpython.org/lib/module-HTMLParser.htmlを参照せよ)。少なくともテキストとハイパーリンクを処理して表示できるようにせよ。挑戦として背景色、テキストの表示形式、画像を処理できるようにせよ。\x0c'}, {'id': '97d04f7a-1c31-4bdb-94b8-75b9158ecf44', 'page': 1, 'text': ''}, {'id': 'df4e9e5a-66bb-408f-ab2c-a17d32a15b47', 'page': 273, 'text': '273付録Aデバッギングプログラムにはさまざまのエラーが起こり得る。エラーをより早く追跡するためにそれらを区別しておくことが大変有益だ。•構文エラーはPythonがソースコードをバイトコードに変換している過程でPython自身によって吐き出されるものである。それらは通常はプログラム上に構文違反があることを示す。例えば、def文の最後にコロンを忘れると、少しばかり冗長なメッセージ、SyntaxError:invalidsyntaxが出る。•実行時エラーはプログラムが実行されている過程で何かおかしなことが起こるとインタプリタによって吐き出される。大抵の実行時エラーメッセージにはどこでそのエラーが発生したか、どの関数を実行中だったかについての情報が含まれる。例えば、無限の再帰処理は最終的には実行時エラー、maximumrecursiondepthexceededになる。•意味的エラーはエラーメッセージが発せられないけれど、正しい結果が得られないという問題である。例えば、ある表現が、あなたが期待したような順序で評価されず、そのため間違った結果になってしまったというような状況である。デバッギングの第一歩はあなたが格闘しているエラーはどのような種類のエラーか見極めることである。以下の節はエラーの種類に従って叙述されるが、いくつかの技法は一つ状況だけでなく他の状況でも適用できるものである。A.1構文エラー\u3000構文エラーはそれが何を意味するか分かれば対処しやすいものである。不幸にして、エラーメッセージはときとして役に立たない。最も頻繁なメッセージはSyntaxError:invalidsysntaxやSyntaxerror:invalidtokenだが、これらは必要な情報をあまり含むものではない。一方、メッセージは問題がどこで発生したかを教えてくれる。実際、Pythonはどこで問題に気づいたかを示す。しか\x0c'}, {'id': '98578e63-b9d5-4a26-ad3b-6213f7468115', 'page': 274, 'text': '274付録Aデバッギングし、これはエラーのある場所であるとは限らない。ときとして、エラーはエラーメッセージの場所より前のことがあり、よくあることはその前の行だったりする。プログラムを少しずつ大きくしているのであれば、そのエラーは新たに追加した個所であると疑ってみることは有益だ。また、プログラムが文献からのコピーである場合には、一字一句の比較が必要である。ときとして、その本が間違いを含んでいるかもしれないので、もし構文エラーらしいものを見つけたとすると、実はそれはその本の間違いかもしれない。以下はよくある構文エラーを避けるいくつかの方法である:1.変数名としてPythonの予約語を使っていないことを確かめる。2.複合文(for、while、if、def)の先頭行の末尾にコロンがあるか確かめる。3.文字列を表すクオート記号は前後で合っているか確かめる。4.多重行文字列を三重クオート(シングルクオートかダブルクオート)で括るとき、末尾が正常に終わっているかを確かめる。閉じていない文字列はプログラムの最後でinvalidtokenのエラーになる。または、次の文字列が現れるまでプログラムは文字列とみなされてしまう。第二のケースではエラーメッセージは全く現れない。5.括弧で展開する表式―(、{、[―を閉じないと、Pythonは次ぎの行も文の一部をみなす。一般に次ぎの行でエラーメッセージが出る。6.条件文の中で==の替わりに=にしてしまう古典的な間違い。7.インデントが意図通りに使われているか調べる。Pythonはタブでも空白でも処理できるが、それらを混在して使うと問題が起こる可能性あり。問題を避ける最善の方法は自動インデント可能なエディタを使うことだ。これで解決しないときは、次ぎの節に進んでほしい。ずうっと修正をしているのに変化なしインタプリタがエラーを指摘しているのに、エラーが見つからないのはあなたとインタプリタとが同一のコードを眺めていない可能性がある。プログラム開発環境をチェックして編集をしているプログラムがPythonが実行しようとしているものであることを確かめること。もし不安ならば、プログラムの先頭に意図的に分かり易い構文エラーを起こす文を挿入してみることだ。再実行してインタプリタがこのエラーを指摘しないとすれば、あなたは更新されたコードを走らせているのでないことが分かる。このようなことが起こるいくつかの犯人を示す:\x0c'}, {'id': '5b06692a-1437-446f-ab10-478477851ee0', 'page': 275, 'text': 'A.2.実行時エラー275•ファイルを編集したが、実行する前に保存するのを忘れた。開発環境によってはこの保存を替わりにやってくれるものもあるが、そうでないものもある。•ファイル名を変更したが、実行しているものは古い名前のものだった。•開発環境が正常に構築されていない。•モジュールを作成していてインポートを使っているとしたら、Pythonの標準モジュール名と同じ名前は使わないようにする。•インポートでモジュールを読み込むことをしているときには、もしもそれが変更されたモジュールであるならば、インタプリタの再起動かreloadコマンドで再読み込みを行うこと。さもないと変更が反映されない。これでも行き詰まってしまったら、’HelloWorld!’のような簡単なプログラムから再出発し、確認が取れているプログラムが正常に動くことを確かめるのも一つの方法だ。そして、徐々に元のプログラムの一部を新規のプログラムに追加して行くようにする。A.2実行時エラープログラムが構文的の正しいとすると、Pyhtonはそれをコンパイルし、少なくとも実行を開始する。次ぎに起こるとしたらどんなエラーだろうか?全く反応なしこの問題の状況はファイルが関数やクラス定義からなるときで、実行を開始するために必要な何ものも発動していない場合である。そのモジュールをクラスや関数を提供する目的のためにだけ必要な場合は意図的に行うことがある。そうでないのであれば実行を開始するために関数を発動させるか、インタラクティブモードで関数の一つを実行しなければならない。以下の「実行の流れ」の項も参照のこと。プログラムが終わらないプログラムが終了しても何もしなかったようにみえるときは、多分に「ハング」した状態にあるためだ。多くの場合それは無限ループや無限の再帰処理に陥ったことを意味している。\x0c'}, {'id': 'af368ace-f102-4238-9830-63e63cee4048', 'page': 276, 'text': '276\n付録A\nデバッギング\n• もしある特定のループが怪しいと思われるときは、\nそのループの直前に\n「ルー\nプ開始」のprint 文を入れ、ループの直後に「ループ終了」のprint 文を入\nれて再実行してみる。もし最初のメッセージが出て、二番目が出ないとすれ\nば、このループが無限ループである。\n• 大抵の場合、無限再帰処理では実行は暫く続き、その後に’RuntimeError:\nMaximum recursion depth exceeded’のエラーが出る。\nこれが状況なら\n「無\n限再帰」\nの項を参考にしてほしい。\nこのエラーが出ないにしても、\n再帰メソッ\nドや関数に問題ありと思うときも「無限再帰」の節の手法が役に立つ。\n• これらのステップが有効でないなら、別の個所のループや再帰処理をテスト\nしてみよう。\n• これでもうまく行かないときは、あなたがプログラムの実行の流れを理解し\nきれていない可能性がある。\n「実行の流れ」の項をみてほしい。\n無限ループ:無限ループがあると思えてその原因になっているループが特定でき\nるときは、ループの終わりにprint 文を挿入し、ループの条件に関わる変数の値\nとループの条件を表示してみる。\n例えばこうだ:\nwhile x>0 and y<0:\n# do something to x\n# do something to y\nprint "x: ",x\nprint "y: ",y\nprint "condition; ", (x>0 and y<0)\nさて、プログラムを再実行すると、そのループを通過する度に上の三行の表示が出\n力される。ループが終了するときには、最後の条件はfalse になるからだ。ルー\nプが止まらないときには、変数x、y の値が表示されるので、なぜこれらの変数が\n正常に更新されないのか検討ができるはずだ。\n無限再帰:\n大抵の場合、\n無限再帰処理では実行は暫く続き、\nその後に’RuntimeError:\nMaximum recursion depth exceeded’ のエラーが出る。もし疑いがある再帰処理\nの個所が特定できたならば、先ず初めに、その処理に基底ケースがあるかどうか\n確かめる。換言すれば、再帰的な実行を止めてその関数またはメソッドがreturn\nに達する条件があるはずだということである。そうでなければ、アルゴリズムの\n再検討と基底ケースの特定が必要となる。基底ケースがあるにも拘わらずそこに\n達していないように思えるときには、その関数またはメソッドの先頭で仮引数を\n'}, {'id': '0ffdf036-33b5-47af-b69f-3ae4a1db41bc', 'page': 277, 'text': 'A.2.\n実行時エラー\n277\n表示するためにprint 文を挿入してみる。再実行してみると、その関数またはメ\nソッドが発動される度にこのprint 文による表示が現れる。仮引数の値が基底ケー\nスの条件の方向に向かっていないとすれば、問題の所在についてのヒントが得ら\nれるはずである。\n実行の流れ:\nプログラムの実行の流れが把握できないときには、\n「関数foo に入る」\n(このfoo に関数名が入る)というようなメッセージを表示するprint 文を関数の\n先頭に挿入する。プログラムを実行してみるとそのprint 文はその関数が発動さ\nれる軌跡となる。\nプログラムを実行すると例外が発生する\n実行時に何か不正があると、Python は例外の名称、発生したプログラムの行番\n号、トレースバックを含んだメッセージを吐き出す。トレースバックは現在実行さ\nれている関数名、その関数を発動した関数、さらにその関数を発動した関数等の\n特定情報である。換言すれは、あなたが現在どこにいるかを示す関数の系列を遡っ\nて表示する。さらにそれらの関数呼び出しが起きた行番号の情報も含まれる。\n最初にすべきことはプログラムのどこでそのエラーが発生したかを特定し、何\nが起きたかを認識できるかどうか確かめることである。以下はよく遭遇する実行\n時エラーとその意味だ:\nNameError:現在存在しない変数名を参照しようとした。局所変数はローカルで\nしか通用しない。それらが定義された関数の外でそれらを参照することはで\nきない。\nTypeError:いくつかの原因がある:\n• 値の代入が不正である。例えば、文字列、リスト、タプルのインデック\nスとして整数以外のものを代入した。\n• 変換記述文字列と変換に渡されるアイテムの間に不一致がある。アイテ\nムの個数の不一致でも、不正な変換でもこのエラーは出る。\n• 関数やメソッドに渡す引数の個数が間違っている。メソッドでは、よく\n定義を眺めること、第一番目の引数はself である。次ぎにそのメソッ\nドを発動させる側を調べる。そのメソッドを発動させるオブジェクトが\n正しい型か、引数は正確かどうかを調べる。\nKeyError:辞書に存在しないキーを使って辞書の要素にアクセスしようとした。\n'}, {'id': 'da36f38f-d3b8-4c07-87bf-1bea9da3e79e', 'page': 278, 'text': '278付録AデバッギングAttributeError:存在しない属性やメソッドにアクセスしようとした。まずスペルをチェックしよう。存在する属性を表示するdirコマンドを使うこともできる。もしそのAttrubuteErrorがオブジェクトはNoneTypeであると表示したら、そのオブジェクトはNoneである。よく遭遇する原因は関数の戻り値を書き忘れたときである。関数の戻り値を与えないで関数定義を終えると、それはNoneを返す。他の例としては、リストに関連するメソッド、例えばsortのような、を使っているときだ。この戻り値はNoneだ。IndexError:リスト、文字列、タプルにアクセスするために使っているインデックスが(それらの長さ-1)を越えている。直ちにエラー発生の直前にインデックスの値と配列の大きさを表示するprint文を挿入する。配列は予期した長さになっているか?インデックスは正常な値を示しているか?Pyhtonデバッガー(pdb)は種々の例外の原因を突き止めるときに役に立つ。それはそのエラーは発生する直前までのプログラムの状態を吟味できるからだ。pdbについてはdocs.python.org/lib/module-pdb.htmlを参照のこと。多くのprint文の追加で出力に埋没print文を多用したデバッギングの問題は出力に埋没することだ。二つの回避方法がある。出力を簡単化するか、プログラムを簡素化するかだ。出力の簡単化は余分なprint文を削除またはコメントにし、理解し易いように表示形式を工夫することだ。プログラムの簡素化はいくつも方法がある。第一に、プログラムで取り組んでいる問題の規模を縮小してみることだ。例えば、リストの検索の問題であれば、問題のリストを小さいリストで行ってみることだ。プログラムがユーザから入力を受け取る部分もあるのであれば、問題の引き起こすに足りる最も簡単な入力にしてみることだ。第二に、プログラムを整理してみる。死文化されたコードは削除、理解し易いようにプログラムを再構成してみることだ。例えば、問題は多重な入れ子に関連するところかという疑いがあるなら、その部分をもっと簡単な構造に書き直してみることだ。また、問題が大きな関数に由来していると思われるときは、この関数を複数の小さい関数に分割し、それらを別々に検証してみる。ときとして最小単位のテストをしようとする過程がバグを発見することに繋がることがある。プログラムがある状況では問題なく、他のケースでは動かないというのであると、このことがヒントになる。同様に、コードの部分的な書き直しは隠されているバグの発見にも役立つ。プログラムには影響しないと思って変更したことがそうでなかったら、これこそバグの在処を教えているようなものだ。\x0c'}, {'id': '23181b9b-9ba4-4842-97f7-7bdacabc513f', 'page': 279, 'text': 'A.3.意味的エラー279A.3意味的エラーある意味この意味的エラーが最も厄介なエラーである。何故なら、インタプリタは何が悪いのかの情報を一切持っていなからである。あなたたけがそのプログラムが為すべきことを知っている。第一に、プログラム全体と観察から得られたプログラムの振る舞いとの間の関連を付けることだ。プログラムが実際に行っていることについて仮説を持つ必要がある。困ることの一つはコンピュータによる実行があまりにも速いことである。プログラムの実行速度はヒトの速度なみに減速され、デバッカが使えたらと思うことがあるかもしれないが、適当の位置にprint文を挿入することの方がデバッガーを起動させ、ブレイクポイントを挿入・除去し、エラーを見つけるためにプログラムを「ステップ実行」させるより往々にして短時間で済む。プログラムがまともではない以下を自問してみよう:•プログラムの中で、実行されるべきなのに実行されていないように思える個所はないか?その機能を実行している個所を見つけ、それが実行されるべきときに実行されていることを確かめる。•起こるはずがないことが起きていることはないか?その機能を実行している個所をみつけ、起こるはずがないときに実行されているか確認する。•期待していない効果を生成しているようなコードの個所はないか?問題のプログラムコードを自分は理解しているか確認しよう。特にそのコードがPythonの他のモジュールの発動に関与しているときはそれが必要だ。発動させている関数のドキュメンテーションをよく読みなさい。小さなプログラムでそれらを使って結果を確かめよう。プログラムを作成するためには、そのプログラムが如何に動くかについてのメンタルモデルが必要だ。もしプログラムを闇雲に書いているとすれば、問題はプログラムにあるのではなく、あなたのメンタルモデルに問題があるのだ。メンタルモデルを修正する最善の方法はプログラムをいくつかの部分(通常は関数やメソッド)に分割し、その分割した部分を個別にテストすることだ。一度メンタルモデルと現実との不一致が確認できれば抱えている問題は解決できるはずだ。勿論、それらの分割したものを再構築し、プログラムを開発するに過程に応じてテストしなければならない。問題に遭遇したら、プログラムに追加する未知の部分は極少量にすべきだ。\x0c'}, {'id': '10f87216-ba77-468d-a417-8dd468217670', 'page': 280, 'text': '280\n付録A\nデバッギング\n大きく不格好な表現があり、それが予想したようには動いていない\n複雑な表現でもそれが読み易く書いてあれば問題ないが、デバッグは難しくな\nる。複雑な表現を一時的な変数への代入の組み合わせで分割するのは有益なこと\nが多い。\n例を上げよう:\nself.hands[1].addCard(self.hands[self.findNeighbor(1)].popcard())\nこれは以下のように書ける:\nneighbor = self.findNeighbor(1)\npickedCard = self.hands[neighbor].popCard()\nself.hands[1].addCard(pickedCard)\n一時的な変数名が追加の情報になるのでこのような明確に展開された表現は読み\nやすい。中間の変数の値や型も表示し、チェ\nックできるのでデバッグも容易だ。\n長い表現のもう一つの問題は評価の順序が期待したものにならないというもの\nだ。例えば、x\n2π の表現をPython に置き換えるとするとき以下のように書いたと\nする:\nx = x / 2 * math.pi\nこれは正しい表式ではない、何故なら乗算と除算は同じ優先度を持つので、左か\nら右への順序で式は評価されるので、xπ/2 が計算されることになる。括弧を追加\nして評価の順序を明確に示しておくとデバッグし易いものになる:\nx = x / (2 * math.pi)\n評価の順序が不安なときはこのように括弧を使うとよい。プログラムが意図した\nように動くという意味で正しく動くばかりでなく、他の人がみてもより読み易く\nなる。\n期待した戻り値を返さない関数やメソッドができてしまった\n複雑な表現を持つ表式を持たせてreturn 文を書くと、その直前でその戻り値を\n表示することができない。ここでも一時的な変数を使うとよい。以下はその例で\nある:\nreturn self.hands[i].removeMatches()\n'}, {'id': '96c68088-9dac-429f-b463-6038ec3328f3', 'page': 281, 'text': 'A.3.意味的エラー281の替わりにcount=self.hands[i].removeMatches()retruncountとする。これでretrun文の直前で戻り値countの値を表示できる。行き詰まってしまった、助けが必要だまず第一に数分間でよいのでコンピュータの前から離れてみる。コンピュータは以下のような症状を引き起こす波動を放射している:•フラストレーションや怒り。•「コンピュータは私を憎んでいる」という妄想や「私が帽子を後ろ向きに被ったとみにのみプログラムは動く」という迷信。•酔歩プログラミング(思いつくままにあれこれとプログラムを変更してみる)。これらの症状の一つにでも当てはまるようだったら、椅子を立ち散歩に行くとよい。落ち着いてきたらプログラムのことを考えてみよう。何が起きているのか?何があのような振る舞いの原因なのか?ちゃんと動いたプログラムだったのかいつか?それに何を追加したのか?ときとして時間が解決してくれるときもある。私はよくバグが見つけられるのはコンピュータから離れ、あれこれを考えているときだ。それは列車の中だったり、シャワーを浴びているときだったり、眠りに落ちる寸前のベッドの中だったりする。もうダメだ、助けが必要だこれは起こる。最高のプログラマでもときとして行き詰まる。一つのプログラムに長い間掛かり切りになっているが故にエラーが見えないことがある。新たな目が必要なことがある。他の人を呼び込む前に充分な用意ができていることを確認しよう。プログラムはできうる限り簡単にすべきだ。また問題を引き起こすに必要な最少の入力データを用意すべきだ。さらに、適当な場所にprint文(その出力も充分に意味が分かるもの)を追加すべきである。最後に、起きている問題を充分に理解できていて、それを簡潔に叙述できる必要がある。他の人に助けを求める前に、その人が必要な情報が揃っているか確かめよう。\x0c'}, {'id': '395ef8ae-191e-4b93-84bd-f5443fbf888a', 'page': 282, 'text': '282付録Aデバッギング•もしエラーメッセージがあるなら、それは何で、プログラムの何処を示しているのか?•このエラーメッセージが発生するようになる直前にあなたは何をしたのか?あなたが最後に書き加えた部分は何処か?失敗した最近の事列は何か?•これまで試したことは何か?それで解ったことは何か?バグは見つかったときは、もっと早く発見するためには何をすればよかったのかを僅かな時間でよいから考えてみよう。次回には同じような状況になったときは今より早くバグを見つけるようになるだろう。そのプログラムが動くようになることだけが目標ではないことを思いだそう。目標は如何にしたら動くプログラムが作れるかを学ぶことだ。\x0c'}, {'id': 'b6a56f93-1bed-4073-bf00-927176a9ebd7', 'page': 283, 'text': '283\n付録B\nアルゴリズムの解析\nこの章のアプローチはAllan B. Downey 著“Think Complexity”(O’Reilly Media:\n2011) から編集した抄録である。今読んでいるこの本を終えてしまったら、そちら\nの本に進んでみたいと思うかもね。\nアルゴリズムの解析(analysis of algorithms)はアルゴリズムの効率、特に\n実行時間とメモリー消費を調べるコンピュータ科学の一分野である。\n(http::/en.wikipedia/wiki/Analysis_of_algorithms を参照せよ)\nこのアルゴリズムの解析の実践的な目標はプログラムを設計するときの目安とし\nて異なったアルゴリズムの実行効率を予測することである。\n2008 年の米国大統領選挙キャンペーンの最中、オバマ候補はグーグル社を訪問\nしたとき前触れなしの質問を受けた。主経営責任者のエリックシュミットは冗談交\nじりに、\n「100 万個の32 ビット整数をソートするのに最も効率のよいのは何でしょ\nうね」と聞いた。\n「バブルソートではダメだよね」と即座に答えたことからどうや\nらオバマ候補は耳打ちされていたようだ。\n(http://www.youtube.com/watch?v=k4RRi_ntQc8 を参照せよ)\n確かにバブルソートは原理的には単純だが、大きなデータセットでは効率が悪\nい。シュミットが求めていた答えは多分「基数ソート」だろう。\n(http://en.wikipedia.org/wiki/Radix_sort を参照のこと)1 \u3000\nアルゴリズムの解析の目標はアルゴリズム間の意味ある比較である。しかし、い\nくつかの問題がある:\n• アルゴリズムの相対的な実行効率はハードウエアの特性によるかもしれない。\n従ってあるアルゴリズムはマシンA ではより速いが、マシンB では他が速\nいということがある。このような状況の解決策はマシンモデルを特定させる\nことだ。そして、この仮想的なマシンモデルの下でアルゴリズムを実行した\nときに必要なステップ数、または操作を解析することだ。\n1しかし、インタビューで同じような質問を受けたとき、もっとましな答えは以下のようだと私\nは思う。\n「100 万個の整数をソートする最速な方法は私が使っている言語が提供しているソート関\n数をまず使うことです。それは大抵のアプリケーションに対して充分な性能を持っています。もし\nそのアプリケーションに対してそれが遅いことが判明したら、プロファイラを使いどの部分で時間\nを食っているのか調べます。そして、より速いソートアルゴリズムがこれに対して充分な効果があ\nると判明したら、基数ソートのうまい実装を探すことにします。\n」\n'}, {'id': '4c9d482f-03b7-4ea4-8fcd-b016833b9d26', 'page': 284, 'text': '284\n付録B\nアルゴリズムの解析\n• 相対的な実行効率はテストに使うデータセットの細部に依存するかもしれな\nい。例えば、あるソートのアルゴリズムは部分的にソートされているデータ\nセットに対してより高速であるが、今の場合は更に低速だというようなこと\nだ。このような事態を回避する通常の方法は最悪の状況で解析することだ。\n平均的な実行効率を解析することも有用だが、平均を取るべきデータセット\nの集まりが何であるかも自明ではない。\n• 相対的な実行効率は問題の大きさにも依存する。小さいリストに対して高速\nのソートのアルゴリズムも大きなリストに対しては低速だということもあり\n得る。この状況に対する解決法は問題の大きさの関数として実行時間(また\nは操作の回数)を表現し、問題のサイズが大きくなって行くにつれての漸近\n的な振る舞いを比較することである。\nこれらの比較の有用な点はそれ自体がアルゴリズムの分類に繋がっていることで\nある。例えば、入力のサイズn に対してアルゴリズムA はn に比例し、アルゴリ\nズムB はn2 に比例するとしよう。大きなn に対してアルゴリズムA はより高速だ\nと期待できる。\nB.1\n増加の次数\n二つのアルゴリズムを解析し、実行時間を入力データの個数n の関数と表現し\nたとしよう。アルゴリズムA はある問題を個数n で解くのに100n + 1 ステップ掛\nかったとしよう。一方アルゴリズムB はn2 ステップであったとしよう。\nこれから異なった問題サイズn に対して二つ実行時間の表にしてみる:\n入力データの\nアルゴリズムA の\nアルゴリズムB の\n個数\n実行時間\n実行時間\n10\n1 001\n111\n100\n10 001\n10 101\n1 000\n100 001\n1 001 001\n10 000\n1 000 001\n> 1010\nこの表からn = 10 ではアルゴリズムA は酷く効率が悪いようにみえる。アルゴ\nリズムB と比較すると10 倍も時間が掛かる。しかし、n = 100 では殆ど同じ、更\nに大きなn に対してはA はかなりよい。\n大きなn に対しての振る舞いの基本的な理由はのn2 項を含む任意の関数はn 項\nを支配項とする任意の関数と比較してより速く増大するからである。支配項とは\n最高の次数を持つ項である。\n'}, {'id': 'c00b1934-2b60-4932-9b70-c677591ee234', 'page': 285, 'text': 'B.1.\n増加の次数\n285\nアルゴリズムA の支配項の係数は100 と大きく、これがnが小さいときにB が\n有利な原因である。しかしこの係数にも拘わらずan2 > bn を満たすn は必ずある。\nこの議論は支配的でない項に対しても当てはめることができる。アルゴリズム\nA の実行時間がn + 1000000 と書けたとしても大きなn に対してはアルゴリズム\nB より優秀だと言える。\n一般に大きな問題に対しては次数の小さい支配項を持つアルゴリズムがより優\n秀だといえるが、小さな問題になってくると他のアルゴリズムが有利になる切り\n換え点がある。この切り換え点はアルゴリズムの細部、入力データ、ハードウエア\nに依存する。従って、アルゴリズム解析では通常は無視する。しかし、この事実\nは忘れてよいわけではない。\nもし二つのアルゴリズムの支配項の次数が同じであるときは、どちらが優秀か\nをいうのは難しい。これも細部による。従ってアルゴリズム解析ではその支配項\nが持つ係数が異なっていてもこの二つは同等だと考える。\n\u3000「増加の次数」はそれらの漸近的な振る舞いが同等とみなされる関数の集合に\n与えられる。例えば、2n、100n、n + 1 は同じ増加の次数に属する。つまり、ビッ\nク-O(訳注:ギリシャ文字)記法(Big-Oh notation)ではO(n) と書く。そして\nこの関数の集合はn に比例して増大するので、この次数を線形(linear)と呼ぶ。\n支配項がn2 である全ての関数はΟ (n2) に属し、それらは方形と呼ばれる。支\n配項がを持つ関数に付けられたものとしてはおかしな名前だ。\n以下の表は通常アルゴリズム解析で登場する増加の次数を劣性が増加する順に\n並べてものである。\n増加の\n名称\n次数\nO(1)\n定数\u3000\nO(logb n)\n対数的(任意の底b で)\nO(n)\n線形\u3000\nO(n logb n)\n「エヌログエヌ」\nO(n2)\n方形(二次)\nO(n3)\n立方形(三次)\nO(cn)\n指数的(任意の底c で)\n\u3000\n対数項の場合その底の値は問題ではない。底が異なることは定数をその項に乗\nずることに同値なので増加の次数を変えるものではないからだ。同様に底が何で\nあろうと指数的な次数の関数は同じ増加の次数に属する。指数関数は急激に増加\nするので、指数的なアルゴリズムは小さい問題でしか使えない。\n'}, {'id': '381d92c3-55d4-4f52-9b5b-b9c8da05e84d', 'page': 286, 'text': '286付録Bアルゴリズムの解析練習問題B.1ビック-Ο記法http://en.wikipedia/wiki/Big_O_notationを読み、以下の問いに答えよ。1.n3+n2の増加の次数は何か?それではこれは1000000n3+n2?ではこれはn3+1000000n2?2.(n2+n)·(n+1)の増加の次数は何か?乗算を始める前に、支配項のみが必要なことを思い出してみよう。3.もしもfが特別でない関数gに対してO(g),のオーダーならば、taf+bについて何が言えるか?4.もしもf1とf2とがO(g)のオーダーであるならば、f1+f2について何が言えるか?5.もしもf1がO(g)のオーダーでf2がO(h)のオーダーであるならば、f1+f2について何が言えるか?6.もしもf1がO(g)のオーダーでf2がO(h)のオーダーであるならば、f1·f2について何が言えるか?実行速度を気にするプログラマはときとしてこの種の解析結果に納得するのが難しい。係数の大きさや支配項でない項が実際の差に影響することがあるからだ。ハードウエアの詳細、プログラム言語、入力データ特性が大きな差を作り出すこともある。そして、小さなサイズの問題では、漸近的な振る舞いは問題とならない。しかしこれらの注意を心に留めて置くとしても、アルゴリズム解析は有益なツールになる。少なくとも、大きなサイズの問題に対しては、「より優秀」と判定されたアルゴリズムは実際にもより優秀であり、ときとしてかなり優秀である。同じ増加の次数を持つ二つのアルゴリズムの差はサイズが大きくなっても定数に留まるが、異なった次数を持つアルゴリズムの差はサイズと共に限界なしで大きくなる。B.2Pythonの基本操作の解析大抵の代数演算は一定時間で実行される。乗算は加算、減算より時間を要する。除算はさらに時間が掛かる。しかし、これらの演算は被演算子の大きさには依らない。極端に大きな整数は例外で、その実行時間は桁数と共に増加する。配列や辞書の要素の読み書きに出てくるインデックスを使って要素を指定する操作はデータ構造の大きさに依らず一定時間で実行される。配列や辞書を横断的に眺めるforループは本体での操作が一定時間である限り、通常は線形だ。例えば、リストの要素の総和を求める操作は線形である:\x0c'}, {'id': '0b4dfdfd-7a24-4ea8-a4e5-90046dc440c8', 'page': 287, 'text': 'B.2.Pythonの基本操作の解析287total=0foraint:total+=a組み込み関数sumも同じことをやっているので線形であるが、もっと効果的な方法を実装している。アルゴリズム解析の言葉では、支配項の係数がより小さいということになる。同じループを文字列のリストの「数え上げ」に適用するとその次数は方形(二次)となる。文字が基本操作の単位であり文字列の連結は線形となるが、その連結の操作のループだからである。(訳注:文字列の連結は既存の文字列の最後に新たな文字列を追加することになり、この既存の文字列の最後を探す操作が線形である。)文字列メソッドjoinは文字列が基本単位であり文字列の全長に対して線形なので、その次数は線形だ。経験からループの本体の次数がO(na)であると、全ループはO(na+1)になる。例外はこのループがある決まった有限回数で終了することが示されたときである。ループはnにも拘わらずk回で終了するのであれば、その次数は如何にkが大きくてもO(na)である。定数kの乗算は次数を変えないし、除算も然りである。従って本体はO(na)であり、ループの回数がn/k回であっても全体の次数はO(na+1)となる。多くの文字列やタプルに対する操作は線形である。例外はインデッスを使ったアクセスと関数lenで、これらは定数である。組み込み関数min、maxは線形である。スライスを使った操作は出力の大きさに比例するが、入力のサイズとは独立である。全ての文字列に対する操作は線形であるが、文字の長さがある定数によって有限であるとすると実行時間の次数は定数となる。その例としては単独文字の文字列に対する操作がある。リストに対する操作は大抵線形である。これには例外がある:•リストの末尾に要素を追加するのは平均にすると一定時間だ。もし領域が不足してより大きな領域の全体をコピーするという事態であるとそれは線形となる。しかし、n回の追加で必要となる実行時間はO(n)であるので、一操作当たりの「償却時間はO(1)になる。•リストの末尾のある要素の削除は一定時間である。•リストのソートはO(nlogn)である。辞書の操作やメソッドの多くは一定時間である。これも例外がある:\x0c'}, {'id': 'f473ebfa-cfde-49b6-aeaa-3f0e124c8f98', 'page': 288, 'text': '288付録Bアルゴリズムの解析•copyの操作は辞書の要素の数に比例する。しかし、要素の大きさにはよらない(これは参照のコピーであって、要素のコピーでないならばのことである)。•updateの操作は引数として受け取る辞書の大きさに比例するが、更新される辞書にはよらない。•メソッドkeys、values、itemsは辞書全体を新規リストとして返すので線形である。メソッドiterkeys、itervalues、iteritemsはイテレータを返すので一定時間である。しかし、forループでそのイテレータを使って横断的な処理をすると、そのループ処理は線形である。関数iterを使うことは初期処理を節約できるが、処理する要素の数が有限でなければ増加の次数は変わらない。辞書の実行速度はコンピュータ科学に於けるちょっとした奇跡である。この話題はB.4節で取りあげる。練習問題B.2ソートのアルゴリズムについてhttp://en.wikipedia.org/wiki/Sorting_algorithmを読み、以下の問いに答えよ。1.「比較ソート」とは何か?比較ソートの最悪状況下での最高実行時間の増加の次数は何か?\u3000また、任意のソートアルゴリズムの増加の次数は何か?2.バブルソートの増加の次数は何か?また、何故にオバマ候補はこれでは「ダメだよね」と思ったか?3.基数ソートの増加の次数は何か?またこのソートが使える前提条件は何か?4.安定ソートとは何か?またその機能が実際問題として問題になるのは何故か?5.最も効率の悪いソートは何か(名前が付いている)?6.C言語のライブラリで使われているソートアルゴリズムは何か?Pythonではどうか?それらは安定ソートか?webで調べることも手かもしれない。7.比較ソート以外のソートの多くは線形である。Pythonでは何故にO(nlogn)の比較ソートを使っているのか?\x0c'}, {'id': '44f500c3-b766-4dd5-b44c-1d56bbcb52ef', 'page': 289, 'text': 'B.3.探索アルゴリズムの解析289B.3探索アルゴリズムの解析探索は配列と目的のアイテムとを受け取りそのアイテムがその配列の中に存在するかどうか、よくあるのは目的アイテムと一致する配列のインデックスを返すアルゴリズムである。最も単純な探索アルゴリズムは「線形探索」である。これは配列のアイテムを一つ一つ横断的に調べ、目的アイテムと一致するものを見つけたら終了する。最悪のばあいは配列の全ての要素を調べることになるので線形である。配列処理に使うin演算子は線形である。find、countのような文字列で使うメソッドも同様である。もし配列が順序付けられているならば、二分探索が使える。この次数はO(logn)である。辞書(本当の)で単語を探すときに使っているアルゴリズムである。最初から一つ一つ調べる替わりに真ん中の単語あたりから当たり、探している単語はそれより前か後かを調べる。それがそれ以前になるならば、前後の前半を調べる。そうでなければ後半を調べる。どちらにしても残りの半分は無視する。配列は1000000要素を持っていたとしても約20回で目的の単語に行き着くか、該当する単語はないという結論を得る。これは線形探索に比較して50,000倍程度高速である。練習問題B.3ソートされたリストと目的の値を引き受けその値がリストの中に存在するときはそのインデックスを返し、該当なしであるときはNoneを返す関数bisectionを作成せよ。または、bisectモジュールのドキュメンテーションを読み、使ってみよう。二分探索は線形探索に比較するとずうっと速いが、配列が順序付けられている必要があり、そのための余分な手間が必要である。ハッショ表と呼ばれているもう一つのデータ構造がある。これはさらに高速である。これは探索を一定時間で行い、しかもアイテムがソートされている必要もない。Pythonの辞書はそのハッシュ表を使って実装されている。従って、in演算子を含め辞書に対する大半の操作は一定時間である。B.4ハッシュ表如何にハッシュ表が有用で、効率がよいかを説明するために、マップの簡単な実装からハッシュ表に到達するまで徐々にそれを改良して行くことにする。\x0c'}, {'id': '23b1f454-69b7-4c36-9211-7d66fef6e21a', 'page': 290, 'text': '290付録Bアルゴリズムの解析この実装を論証するためにPythonを用いる。しかし、実際にはそのようなコードをPythonで書くことはないだろう。何故ならPythonでは辞書型を使えばよいからだ。この章では辞書型が存在しない世界を想像し、キーから値を写像するデータ構造を実装したいのだと思ってほしい。実装したい操作は以下のようだ:add(k,v):キーkから値vの写像の追加。Pyhtonの辞書型dではd[k]=v。get(target):targetがキーであるような値を探索し、その値を返す。Pyhtonの辞書型dではd[target]またはd.get(target)。さて以下では各キーは唯一であると仮定しよう。このようなインタフェースの最も簡単な実装はキーと値のペアをタプルとするリストにすることだ:classLinearMap(object):def__init__(self):self.items=[]defadd(self,k,v):self.items.append((k,v))defget(self,k):forkey,valinself.items:ifkey==k:returnvalraiseKeyErrorメソッドaddはキー・値のペアの一つをアイテムのリストに追加する。これは(マップの大きさに関係なく)一定時間でできる。メソッドgetは一つのforループ使ってtargetキーを探索しその値を返す、なければKeyErrorの例外を発生させる。この処理は(マップの大きさに比例して時間がかかるので)線形である。別な方法は予めリストをキーでソートしておくことが考えられる。これであるとメソッドgetには二分探索が使えるので、この処理時間はO(logn)のオーダーだ。しかし、新規のアイテムをリストに挿入するためにはリストの横断的処理が必要なのでこの処理は線形になる。従ってこれはあまり期待できない。その他、メソッドaddとメソッドgetを共にO(logn)のオーダーにするデータ構造(http://em.wikipedia.org/wiki/red-black_tree)もあるが、目標の一定時間処理ではない。先に行こう。LinearMapを改良する一つの方法はキー・値ペアのリストを小さいリストに分割することだ。以下はその実装でBetterMapと名付けた。このリストは100個の\x0c'}, {'id': '74e92457-b85c-401c-b4a6-47e571eb6b27', 'page': 291, 'text': 'B.4.\nハッシュ表\n291\nLinearMap からなる。直ぐにわかるがメソッドget は最終的にはLinearMap の一つ\nを横断的探索しなければならないので線形のオーダーである。しかし、BetterMap\nはハッシュ表へ向かう第一歩である:\nclass BetterMap(object):\ndef __init__(self, n=100):\nself.maps = []\nfor i in range(n):\nself.maps.append(LinearMap())\ndef find_map(self, k):\nindex = hash(k) % len(self.maps)\nreturn self.maps[index]\ndef add(self, k, v):\nm = self.find_map(k)\nm.add(k, v)\ndef get(self, k):\nm = self.find_map(k)\nreturn m.get(k)\nメソッド__init__はLinearMap を複数個入れるリストを作成する。\nメソッドfind map は組み込み関数hash を使う。この関数はPython の殆ど全ての\n型を引数として整数を戻す。実装で唯一の制限はハッシュ可能なデータ型、つま\nり変更不可のデータ型であることだ。リストや辞書はハッシュには使えない。ハッ\nショ化するオブジェクトが同じであるとみなされると同じ整数が返ってくる、し\nかし逆は必ずしも成り立たない。異なったオブジェクトが同じハッシュ値を持つ\nこともある(だから同一のLinearMap に保存される)\n。find map はまたモジュラ\n演算子を使ってハッシュ値を0 からlen(self.maps) までの整数値に包み込んで\nいる。従ってリストの大きさの制限を満たす整数値になっている(勿論このこと\nによって異なったハッシュ値が同一のインデックス値をもつことにもなる)\n。しか\nし元のハッシュ値に大きな片寄りがなければ(これはハッシュ関数の設計による\nが)\n、個々のLinearMap のアイテムの個数はn/100 に近いと期待できる。\nLinearMap の実行時間はアイテムの数に比例するので、BetterMap の実行速度\nはLinearMap の100 倍になる。しかし、LinearMap のオーダーは線形のままなの\nでBetterMap も線形である。支配項の係数は小さくなったのはよいことだが、ハッ\nシュ表のようには行かない。\n'}, {'id': '6fa6342a-3ba3-4462-a12f-49c5631680de', 'page': 292, 'text': '292\n付録B\nアルゴリズムの解析\nここでハッシュ表を高速にする決定的な考えを導入する。LinearMap の長さを有\n限にできれば、メソッドget は一定時間でできる。一つのLinearMap 当たりのア\nイテムの数の増加を監視し、制限値を超えたら複数のLinearMap を追加してハッ\nシュ表を再構成する。以下のその実装である:\nclass HashMap(object):\ndef __init__(self):\nself.maps = BetterMap(2)\nself.num = 0\ndef get(self, k):\nreturn self.maps.get(k)\ndef add(self, k, v):\nif self.num == len(self.maps.maps):\nself.resize()\nself.maps.add(k, v)\nself.num +=1\ndef resize(self):\nnew_maps =\nBetterMap(self.num * 2)\nfor m in self.maps.maps:\nfor k,v in m.items:\nnew_maps.add(k,v)\nself.maps = new_maps\n各HashMap は一つのBetterMap を含んでいる。メソッド__init__ではそれをたっ\nた二つのLinearMap で始める。変数num はリスト全体に保存するアイテムの個数を\n監視するのに使う。メソッドget はBetterMap のそれをそのまま実行する。核心は\nメソッドadd で、まずアイテムの個数とBetterMap のサイズ(つまり、LinearMap\nの個数)を比較する。それらが等しいとき、つまりLinearMap 一つ当たりのアイテ\nムの数が平均で1 であるときには、resize が呼び出される。メソッドresize はそ\nれまでの二倍の個数のLinearMap を持つ新規のBetterMap を生成し、それまでの\nBetterMap に保存されているアイテムを\n「再ハッシュ」し、この新規マップに保存\nする(約半数の個数のLinearMap で充足する)\n。この「再ハッシュ」はfind maps\n'}, {'id': '3d284a25-c177-4daf-bcde-ea9819514c63', 'page': 293, 'text': 'B.4.ハッシュ表293で使っているモジュラ演算子の分母であるLinearMapの個数が変わるので必要である。これで同じLinearMapに割り当たされてしまったいくつかオブジェクトは分散することになる(このこと自体は期待したことだよね)。「再ハッシュ」はアイテムの個数に比例するので線形であり、resizeも線形である。これだけではaddメソッドがアイテムの個数に関わらない定数時間でできるという約束からすると期待外れのようにみえるかもしれない。しかし、resizeは毎回する必要はない、従って操作addは、いつもは定数時間で、時々線形になる。n回のaddを実行するのに必要な操作はnに比例することになるから操作add一つあたりの時間はアイテムの数に関わらない定数時間となる。このことを明らかにするために空のハッシュ表から初めて、次々とアイテムを追加して行くと考えよう。2つのLinearMapがあるので最初の2つのaddは速い。例えば各々に必要とする時間を1単位としよう。次ぎのaddはresizeが必要だ。そこで最初の2つは「再ハッシュ」される(それには全てで2単位の時間が必要だ)。そして三番目のアイテムをaddするが、これは1単位時間が必要だ。次ぎは1単位、全てで6単位が4つのアイテムに対して必要になる。さて次はresize(四のアイテムの)と一個の新規アイテムのaddで5単位必要になる。しかし引き続く3アイテムは各1単位で済むから、これで8アイテムに対して必要な時間は8+5+3=14単位になる。さらに次ぎのaddはresize(8アイテムの)と一個の新規アイテムのaddで9単位、しかしそれ以降の7アイテムは各々1単位だから16個のアイテムに対して30単位の時間を使う。32個のアイテムのaddの処理で62単位が必要となるといった具合で、もうあなたもからくりが分かったと思う。nが2のべき数であるとき、n個のaddに要する時間は2n−2単位になる。従って一つadd操作当たり2単位余りの時間が必要となる。nが2のべき数のときが最高の効率で、それ以外は若干落ちるがそれは重要ではない。重要なことがaddの処理がO(1)であることだ。図B.1:ハッシュ表におけるadd操作のコスト\u3000図B.1は視覚的な説明の図である。各ブロックは作業の単位時間を表す。列は左から右へと各addで必要とする作業時間を表現している。「再ハッシュ」で要する作業時間は縦のタワーの系列として表現されている。このタワーを倒して基\x0c'}, {'id': 'd148ac7e-c6a9-4f99-b954-d0277771a12b', 'page': 294, 'text': '294付録Bアルゴリズムの解析本的なaddに要する時間ブロックに重ねてみるとn個のアイテムを追加するときに必要な作業時間は2n−2単位であることが視覚的にも分かる。このアルゴリズムの重要な特徴はハッシュ表のサイズを大きくして行くときに、幾何級数的に増やしていることだ。もしサイズを算術的に増加させる、つまり毎回一定の固定した数だけ増やすと操作add一つ当たりの平均時間は線形となる。ハッシュマップの実装はhttp://thinkpython.com/code/Map.pyからダウンロードできる。しかし、Pythonではそれを使う必要はない。単に辞書を使えば済むからだ。\x0c'}, {'id': '8cbfa068-9f0f-42f1-aca0-528a343e95a7', 'page': 295, 'text': '295付録CLumpyついてこの本を通してプログラムの状態を表現するために種々の図を使ってきた。第2.2節では変数の名前と値を示す状態図を用いた。第3.10節では関数が呼ばれる度に一個フレームを示すかたちでスタック図を導入した。各フレーム内には各関数またはメソッドの仮引数と局所変数を示した。再帰関数のスタック図は第5.9節及び第6.5節に登場した。第10.2節ではリストが状態図でみるとどうなるかみた。また同様に第11.4節では辞書についてみた。第12.6節ではタプルの状態図を二つの方法で示した。第15.2節ではオブジェクト図を導入した。そこでは一つのオブジェクトをその属性またそのまた属性の属性、等々というかたちで示した。第15.3ではRectangleクラスのオブジェクト図を例証し、埋め込まれたオブジェクト、Pointを議論した。第16.1節ではTimeオブジェクトの状態を観察した。第18.2節ではそれぞれが固有の属性を持っている一つのクラスオブジェクトと一つのインスタンスのオブジェクト図をみた。最後に、第18.8節では、プログラムを構成しているクラスとそれらの間の関係を示したクラス図をみた。これらの図は統一モデリング言語(UML)を基礎にしている。これはソフトウエア工学研究者間でプログラム設計、特にオブジェクト指向プログラムに関する情報交換をするために用いられている標準化されたグラフィック言語である。UMLはオブジェクトやクラスの間の種々の関係を図表で表現することができる豊富な機能を持つ言語である。この本で使ったものはその内の小さなサブセットであるが、実践に用いられる度合いが高いサブセットである。この付録の目的はいくつかの章で示した図を再認識することとLumpyを紹介することである。Lumpyは文字を並び替えてあるが“UMLinPython”の略であり、パッケージSwampyの一部である(第四章、第十九章の事例研究、練習問題15.4を手掛けた読者は既にインストールしているはずである)。Lumpyは実行中のプログラムの状態を調べ、オブジェクト図(スタック図を含め)やクラス図を生成するためにPythonモジュールinspectを使っている。\x0c'}, {'id': '55accb59-466c-43c4-bbfb-d38266049d3e', 'page': 296, 'text': "C.1 状態図\n以下は状態図を生成するためにLumpyを使った例である:\n\n```python\nfrom swamy.Lumpy import import Lumpy\nlumpy = Lumpy()\nlumpy.make_reference()\n\nmessage = 'And now for something completely different'\nn = 17\npi = 3.141592653589793\n\nlumpy.object_diagram()\n```\n\n第一行目は`swamy.Lumpy`からLumpyクラスをインポートしている。もしSwampyをパッケージとしてインストールしていない場合は、Swampyの関連ファイルがPythonの検索パスにあることを確認して以下のインポートを書く:\n\n```python\nfrom Lumpy import import Lumpy\n```\n\n次はLumpyオブジェクトの生成で、「参照・ポイント」つまりそれぞれで確認されたオブジェクトとLumpyが記録することを意味するポイントを設定する。その次には、プログラムの基本で新規の変数の数の意味が示され、最後に`object.diagram`を呼び出し、参照ポイントに関連されたオブジェクトの、1のばあいはn, piを作図する。図C.1は結果である。グラフィカルな表現は結果に示した。\n\n図C.1: Lumpyで生成された状態図\n\nの異なりについて。例えば、個人への参照は変数の間に言及されるが個人を直接示すことはない。反対に、図はリスト項目である。しかし、図が表現している内容は同一である。変数は``を引き出す枠の中のものである。このことはそれらの変数を追加して、図がどうなるのか考えてみよう。"}, {'id': '85a0215a-5573-4cff-9972-5e3cc7d854de', 'page': 297, 'text': "C.2 スタック図\n=================\n\n以下はLumpyを使ってスタック図を描いたものである。 \n[http://thinkpython.com/code/lumpy_demo2.py](http://thinkpython.com/code/lumpy_demo2.py) からダウンロードできる。\n\n```python\nfrom swampy.Lumpy import Lumpy\n\ndef countdown(n):\n if n <= 0:\n print('Blastoff!')\n lumpy.object_diagram()\n else:\n print(n)\n countdown(n-1)\n\nlumpy = Lumpy()\nlumpy.make_reference()\ncountdown(2)\n```\n\n図 C.2 は結果である。各フレームは関数名を外側に変数を中に入れた枠で表現されている。この場合、再帰関数なので再帰の深さの色レベルで一つのフレームが割り当てられている。スタック図は実行中の特定の時間のプログラムの状態を示している。\n\n![図 C.2 スタック図](path/to/your/image.png)\n\n---\n\nC.3 オブジェクト図\n=================\n\n第 10.1 節のリストに対するオブジェクト図を例として示す。 \n[http://thinkpython.com/code/lumpy_demo3.py](http://thinkpython.com/code/lumpy_demo3.py) からダウンロードできる。"}, {'id': '0c5fa7b3-afd5-44e9-94bd-81335289ccbd', 'page': 298, 'text': "```markdown\n# 付録 C: Lumpy について\n\n```python\nfrom swampy.Lumpy import Lumpy\n\nlumpy = Lumpy()\nlumpy.make_reference()\n\ncheeses = ['Cheddar', 'Edam', 'Gouda']\nnumbers = [17, 123]\nempty = []\n\nlumpy.object_diagram()\n```\n\n図 C.1 は結果である。リストはインデックスが要求されていることを示すボックスとして表示されている。この表現は実際にはインデックスはリストの一部ではないので誤解を与えるかもしれない。しかし、これで図は読み易くなっていると思う。空のリストは空のボックスとして表示されている。\n\n図 C.2: オブジェクト図\n\n次は第 11.4 節で扱った辞書に対する例である。 からダウンロードできる。\n\n```python\nfrom swampy.Lumpy import Lumpy\n\nlumpy = Lumpy()\nlumpy.make_reference()\n\nhist = histogram('parrot')\ninverse = invert_dict(hist)\n\nlumpy.object_diagram()\n```\n```"}, {'id': 'ed056251-ea19-493e-a00c-0c895e3ee0ed', 'page': 299, 'text': '# C.3. オブジェクト図\n\n図 C.4 は結果である。`hist` は文字(1 文字の文字列)から整数への写像になっている辞書であり、`inverse` は整数から文字のリストへの写像である。以下は第 15.6 節で扱った `Point` オブジェクトと `Rectangle` オブジェクトの対応するオブジェクト図である。\n\n[http://thinkpython.com/code/lumpy_demo5.py](http://thinkpython.com/code/lumpy_demo5.py) からダウンロードできる。\n\n```python\nimport copy\nfrom swampy.Lumpy import Lumpy\n\nlumpy = Lumpy()\nlumpy.make_reference()\n\nbox = Rectangle()\nbox.width = 100.0\nbox.height = 200.0\nbox.corner = Point()\nbox.corner.x = 0.0\nbox.corner.y = 0.0\n\nbox2 = copy.copy(box)\n```'}, {'id': 'f5bb3187-d597-4f29-b0c6-48e6ae57dec9', 'page': 300, 'text': '```\nlumpy.object_diagram()\n\n図C.5は結果である。`copy.copy`は浅いコピーであるので、インスタンスboxとbox2は独自のwidth, heightをもつが、埋め込まれたオブジェクトPointは共有である。この相の共有は変更可のオブジェクトでは問題ないが、変更可能な型ではエラーを起こしやすい。\n\n図C.5: オブジェクト図\n\n## C.4 関数とクラスオブジェクト\n\nオブジェクト図を作成する際はまず黒白を設定する前に関数やクラスの定義をしておいた。こうすることで、関数やクラスオブジェクトは目に現れない。しかし、関数やクラスを引き渡すようなときには、それも現れてほしい、それがどのようなものであるかを例示して示す。\n\n[http://thinkpython.com/code/lumpy_demo6.py](http://thinkpython.com/code/lumpy_demo6.py) からソースはダウンロードできる。\n\n```python\nfrom swampy.Lumpy import Lumpy\n\nlumpy = Lumpy()\nlumpy.make_reference()\n\nclass Point(object):\n """Represent a point in 2-D space."""\n```\n```'}, {'id': 'b3e73491-839a-45d3-83d9-8463caddfeb1', 'page': 301, 'text': '# C.5 クラス図\n\n図 C.6: オブジェクト図\n\n```python\nclass Rectangle(object):\n """Represents a rectangle."""\n \n def instantiate(constructor):\n """Instantiates a new object."""\n obj = constructor()\n lumpy.object_diagram()\n return obj\n\npoint = instantiate(Point)\n```\n\n図 C.6 が結果である。`object_diagram`を一つの関数内で発動させたので、モジュールレベルの変数と関数`instantiate`の発動によるスタック図が得られる。モジュールレベルでは、`Point`と`Rectangle`はクラスオブジェクト(type 型)を参照し、`instantiate`は関数オブジェクトを参照している。この図は次の三つの点を明記しているかもしれない。それは以下の(1)クラスオブジェクト`Point`と`Point`のインスタンスの間の違い、(2)`instantiate`が定義されるときに生成される関数オブジェクトとその関連が呼ばれるときに生成されるフレームの違いである。\n\n## C.5 クラス図\n\n状態図、スタック図、オブジェクト図と区別したがそれらはほぼ同じものである。それらは実行時のある場面でのプログラムの構造を表している。\n\nクラス図には以下のことが見られる。これはプログラムを構成しているクラスとそれらのクラス同士の関係を表現している。クラス図には持続的な時点のプログラムの状態の構造はなく、プログラム全体の構造を記述するという意味で時間に依存しない。例えば、クラスAのインスタンスが一般的にクラスBの参照を含んでいるとき、これらのクラスの図には`HAS-A`関係があることを示す。以下に`HAS-A`。'}, {'id': '28316aef-6db6-4863-a9bb-eb7802dfd503', 'page': 302, 'text': '302付録CLumpyついて関係を示す一例である。このコードはhttp://thinkpython.com/lumpy_demo7.pyからダウンロードできる。fromswampy.LumpyimportLumpylumpy=Lumpy()lumpy.make_reference()box=Rectangle()box.width=100.0box.height=200.0box.corner=Point()box.corner.x=0.0box.corner.y=0.0lumpy.class_diagram()図C.7は結果である。各クラスはクラス名、提供するメソッド名、クラス変数名、インスタンス変数名を含むボックスで表現されている。今回の例では、Rectangle、Pointともインスタンス変数を持つが、メソッド、クラス変数は持っていない。RectangleからPointに向かっている矢印は埋め込まれたオブジェクトPointがobjectRectanglecornerheightwidthPointxy図C.7:クラス図含まれていることを示す。加えて、RectangleとPointはobjectを継承している。このことは白抜きの矢印で示されている。もう少し複雑な例を練習問題18.6の解答例を使って示してみよう。このコードはhttp://thinkpython.com/code/lumpy_demo8.pyからダウンロードできる。さらに、http://thinkpython.com/code/PokerHand.pyも必要である。fromswampy.LumpyimportLumpy\x0c'}, {'id': 'ad89e3b0-6647-4efd-af25-481595638011', 'page': 303, 'text': 'C.5.クラス図303fromPokerHandimport*lumpy=Lumpy()lumpy.make_reference()deck=Deck()hand=PokerHand()deck.move_cards(hand,7)lumpy.class_diagram()図C.8が結果だ。PokerHandはHandを継承し、このHandはDeckを継承している。DeckとPokerHandも埋め込みオブジェクトCardを持っている。Handも属性objectDeck__init____str__add_cardmove_cardspop_cardremove_cardshufflesortcardsHand__init__PokerHandhas_flushsuit_histcardslabelCard__cmp____init____str__rank_namessuit_namesranksuit図C.8:クラス図cards(そしてlabelも)を持っているはずだが図には表示されていない。これはこのプログラムではHandのインスタンスが何もないからである。この例はLumpyの限界をも示したものになっている。つまり、そのプログラムではインスタンス化されたオブジェクトについてのみ属性やHAS-A関係が認識されているわけである。\x0c'}, {'id': '32599f44-b099-43fa-af15-849a349ec6d3', 'page': 1, 'text': ''}, {'id': 'ba5897bc-be82-4d51-aa30-209b2c95a734', 'page': 305, 'text': '305付録D日本語の処理ここではPythonで日本語の文字を扱うときのポイントを述べる。日本語の文字をコンピュータで扱うときの問題点は第一にどのような文字が扱えるとよいかということがある。ひらがな、カタカナは文字数が少ないからよいが漢字は文字数が多いから漢字を完璧に扱えるようにするには大変である。「超漢字」システム(http://ja.wikipedia.org/wiki/超漢字)は十七万漢字を取り扱うことができるが、これは例外的である。よく使われる漢字に対してJIS第一水準漢字、JIS第二水準漢字として規格化されている約一万個の漢字がある(https://ja.wikipedia.org/wiki/JIS漢字コード)。後発ではあるがこれとは別の体系としてユニコード(Unicode)がある。東アジア圏の言語を纏めたCKJ統合漢字の中で扱える漢字が約二万個ある(http://ja.wikipedia.org/wiki/Unicodeを参照せよ)。ユニコードで扱える漢字はJIS第一水準漢字、JIS第二水準漢字を完全に含んでいる。一方漢字をどのように符号化するかも複雑である。歴史的な経緯からJISの水準漢字に対しては、シフトJIS、JIS、EUC-JPの三つエンコード方式(符号化方式)が使われている。ユニコードに対してはUTF-8のエンコード方式がよく使われる。Pythonではこの四つのエンコード方式をサポートしている。これらのエンコード方式は全て8ビットの数値を一文字として扱う。漢字一文字も複数バイトに分解して扱うので8ビット文字列と呼ぶ。D.1ユニコード文字列の生成\u3000これに対してユニコード文字列は英数字、ひらがな、カタカナ、漢字の一文字を平等に一文字として扱うことができる。Pythonはこのユニコード文字列で文字列処理を行う機能を持っている。ユニコード文字列を生成する方法の一つが8ビット文字列の前に「u」をつける方方式である:a=’あいう’au=u’あいう’\x0c'}, {'id': 'b28191d3-66fe-459f-8f6d-dea6d7dd50b1', 'page': 306, 'text': '306付録D日本語の処理printlen(a),len(au)このprint文で文字数を表示すると93とでる。つまり、ユニコード文字列ではひらがな文字(カタカナ、漢字も)も文字数が正確に計測される。このようにユニコード文字列で日本語を扱うと英数字の文字列に使えた関数やメソッドがそのまま日本語にも適用できる。さて、インタラクティブ・モードであれスクリプト・モードであれそこに現れた日本語は何らかエンコード方式を使ってコード化されたものである。このエンコード方式を確かめる簡単な方法がある:a=’あいう’au=unicode(a,’utf-8’)これはaに代入された文字列がutf-8でエンコードされたものみなし、ユニコードに変換を試みる関数unicodeを使った例である。第二の引数に間違ったエンコード方式を指定するとエラーとなる。例を示す:>>>a=’あ’>>>unicode(a,’utf-8’)Traceback(mostrecentcalllast):File’’,line1,inunicode(a,’utf-8’)UnicodeDecodeError:’utf8’codeccan’tdecodebyte0x82\\inposition0:unexpectedcodebyteこのエラーが吐き出されないエンコード方式がこの日本語をエンコードしているものだとわかる。D.2エンコード方式の指定スクリプト・モードではファイルの先頭もしくは二行目にそのファイルの日本語のエンコード方式を明示的に指定できる:#coding:sjisこのようにエンコード名(今の場合はシフトJIS)を指定するとこのファイル内の日本語はこの指定したエンコード方式でエンコードされることになる。このエンコード方式を明示することで簡単にユニコード文字列が生成できる。つまり、前に紹介したように文字列の前に「u」を付ける:\x0c'}, {'id': 'bc58cc07-2b60-4d8d-a416-a89ba231dfa3', 'page': 307, 'text': 'D.3.\nユニコード文字列のエンコード変換\n307\nu’ あいう’\nである(インタラクティブ・モードではこの指定はできないので「u」を付ける方\n法では期待したものにならない場合があるので注意)\n。\nPython で使えるエンコード名は以下の表のようになる:\nエンコード名の名称\nPython で使うエンコード名\nシフトJIS\nsjis, shift-jis, shift jis\nJIS\niso-2022-jp\nEUC-JP\neuc-jp\nUTF-8\nutf-8\nユニコード文字列の生成の第二の方法はunicode 関数を使うものだ:\na = ’ あいう’\nau = unicode(a, ’sjis’)\n関数の第二の引数はエンコード名で明示したエンコード名を入れる。\n第三の方法は文字列メソッドを使う方法である。\na = ’ あいう’\nau = a.decode(’sjis’)\nここでもメソッドの引数には指定したエンコード名が入る。\nさて、日本語のエンコード方式にはいくつかあることが分かったが、どのエン\nコード指定を使えばよいのだろうか?スクリプト\n・モードでは日本語をユニコード\n文字列として扱うことが理想であるので、ユニコードを一貫的に扱えるエンコー\nド方式であるUTF-8 が妥当ということになる。\nD.3\nユニコード文字列のエンコード変換\nユニコード文字列を明示的な仕方で8 ビット文字列に変換したいときには、ユ\nニコード文字列のメソッドencode を使う:\nau = u’ あいう’\na = au.encode(’utf-8’)\nこれでユニコード文字列au はutf-8 でエンコード変換され変数a に代入される。\n'}, {'id': '32f2de19-0d76-480a-abc5-c54ef2f475aa', 'page': 308, 'text': '308付録D日本語の処理D.4辞書やタプルで日本語日本語を辞書やタプルで使ってみる。辞書、リスト、タプルの要素としてユニコードを使う。以下はその例である:#coding:utf-8d={u’甲’:(u’コウ’,u’きのえ’),u’乙’:(u’オツ’,u’きのと’),u’丙’:(u’ヘイ’,u’ひのえ’),u’丁’:(u’テイ’,u’ひのと’),u’戊’:(u’ボ’,u’つちのえ’)}forkey,valueind.items():printkey,value結果の表示は以下のようになる。乙(u’\\u30aa\\u30c4’,u’\\u304d\\u306e\\u3068’)丙(u’\\u30d8\\u30a4’,u’\\u3072\\u306e\\u3048’)丁(u’\\u30c6\\u30a4’,u’\\u3072\\u306e\\u3068’)甲(u’\\u30b3\\u30a6’,u’\\u304d\\u306e\\u3048’)戊(u’\\u30dc’,u’\\u3064\\u3061\\u306e\\u3048’)ユニコード文字列が辞書のキーに使えることが分かる。この例では辞書の値はユニコード文字列のタプルになっている。この値のタプル表示はユニコードのコードポインタに対する数字である(この状況はユニコード文字列を辞書などのデータ構造の要素とする場合に起こる)。これを対応する文字列として表示するためにはタプルの要素を直接表示するとよい。つまりforkey,(v1,v2)ind.items():printkey,v1,v2結果は以下のようになる:乙オツきのと丙ヘイひのえ丁テイひのと甲コウきのえ戊ボつちのえ\x0c'}, {'id': 'cf34993f-0c3d-4deb-bde1-a8fbdffdea27', 'page': 309, 'text': 'D.5.日本語を含むファイル309D.5日本語を含むファイル簡単な日本語を含むテキストファイルを作成する。ファイル名はabc.txtで文字コードはsjisとする。このファイルをPythonで作成する。以下はそのプログラムである:#coding:utf-8fout=open(’abc.txt’,’w’)uword=u’あいうえお’fout.write(uword.encode(’sjis’))fout.close()ユニコード文字列をsjisでエンコード変換してファイルに書き込んだ。今度はこのファイルを読み込んでみよう。プログラムは以下である:#coding:utf-8fin=open(’abc.txt’,’r’)forlineinfin:word=line.strip()printword,len(word)uword=unicode(word,’sjis’)printuword,len(uword)fin.close()wordは8ビット文字列(sjis)で、uwordはユニコード文字列である。結果の表示は以下のようになる:あいうえお10あいうえお5文字列の長さをlenで調べてとユニコード文字列は日本語の長さを適切に処理していることが分かる。データベースに使ってみる第十四章で扱ったデータベースにユニコード文字列を使ってみる。#coding:utf-8importanydbmimportpickled={u’子’:u’ね’,u’丑’:u’うし’,u’寅’:u’とら’,u’卯’:u’う’,u’辰’:u’たつ’}\x0c'}, {'id': 'a41722ac-f155-4af2-a0ae-60baf9dc2bbd', 'page': 310, 'text': '310付録D日本語の処理db=anydbm.open(’abc.db’,’c’)forkey,valueind.items():printkey,valueskey=pickle.dumps(key)svalue=pickle.dumps(value)db[skey]=svaluedb.close()データベースのキーや値としてユニコード文字列を直接使うことができないので、dumps関数でユニコード文字列をstringに変換して使う。このデータベースの読み込みには、loads関数を使い元のユニコードに戻す。一例を示す:#coding:utf-8importanydbmimportpickledb=anydbm.open(’abc.db’)forskey,svalueindb.items():key=pickle.loads(skey)value=pickle.loads(svalue)printkey,len(key),value,len(value)db.close()表示の結果は以下のようになる:丑1うし2寅1とら2卯1う1子1ね1辰1たつ2日本語文字の長さが正確に計測されているのが分かる。[参考文献]柴田\u3000淳「みんなのPython」(ソフトバンククリエイティブ:2006年)\x0c'}, {'id': '94a7b5e5-d462-4cf5-b384-5d3df3428bf0', 'page': 311, 'text': '311訳者あとがきAllenDowney著“ThinkPython:HowtoThinkLikeaComputerScientist”(2.0.3版)の日本語訳である。訳者も大学の情報科学系の学生に初級プログラミングの担当をしたことがある。C言語を取りあげたがいくつかの問題点を感じていた。それらは、1.C言語の教育にも拘わらずポインタは難易度が高いとして省略せざるをえない。D.KnuthによればポインタはC言語の宝庫である。2.オブジェクト指向プログラミングについては別の言語で学習する必要がある。3.日本語は継子扱いである。Pythonはこれらの問題点のほとんどを解決してくれると訳者には思えた。Pythonの大きな難点は日本語で読めるよい教科書がないことである。そのようなときに出会ったのがこのThinkPythonである。原著者の「はじめに」にもあるように、この本は大学のプログラミングの教科書として書かれたものでる。教科書として特徴的なことはプログラムを作成する上で不可欠のデバッグについて多くのページを割いていることである。この点が原書を翻訳してみようとした大きな動機でもある。この訳書が如何にプログラミングをするかといったことに感心がある読者にとって助けになれば幸である。日本語の取り扱いについては原著では一切触れていない。訳書では付録DとしPythonによる日本語の処理を纏めておいた。相川利樹仙台\x0c'}] ================================================== **Elapsed Time: 14.78 seconds** ================================================== SERVICES Services: [{'type': 'llm_formatting', 'model': 'gpt-4o', 'input_tokens': 25699, 'output_tokens': 527, 'total_tokens': 26226}, {'type': 'llm_formatting', 'model': 'gpt-4o', 'input_tokens': 25699, 'output_tokens': 210, 'total_tokens': 25909}, {'type': 'llm_formatting', 'model': 'gpt-4o', 'input_tokens': 25699, 'output_tokens': 352, 'total_tokens': 26051}, {'type': 'llm_formatting', 'model': 'gpt-4o', 'input_tokens': 25699, 'output_tokens': 600, 'total_tokens': 26299}, {'type': 'llm_formatting', 'model': 'gpt-4o', 'input_tokens': 25699, 'output_tokens': 389, 'total_tokens': 26088}, {'type': 'llm_formatting', 'model': 'gpt-4o', 'input_tokens': 25699, 'output_tokens': 360, 'total_tokens': 26059}, {'type': 'llm_formatting', 'model': 'gpt-4o', 'input_tokens': 25699, 'output_tokens': 406, 'total_tokens': 26105}, {'type': 'llm_formatting', 'model': 'gpt-4o', 'input_tokens': 25699, 'output_tokens': 489, 'total_tokens': 26188}, {'type': 'llm_formatting', 'model': 'gpt-4o', 'input_tokens': 25699, 'output_tokens': 308, 'total_tokens': 26007}, {'type': 'llm_formatting', 'model': 'gpt-4o', 'input_tokens': 25699, 'output_tokens': 262, 'total_tokens': 25961}, {'type': 'llm_formatting', 'model': 'gpt-4o', 'input_tokens': 25699, 'output_tokens': 506, 'total_tokens': 26205}, {'type': 'llm_formatting', 'model': 'gpt-4o', 'input_tokens': 25699, 'output_tokens': 293, 'total_tokens': 25992}, {'type': 'llm_formatting', 'model': 'gpt-4o', 'input_tokens': 25699, 'output_tokens': 439, 'total_tokens': 26138}, {'type': 'llm_formatting', 'model': 'gpt-4o', 'input_tokens': 25699, 'output_tokens': 462, 'total_tokens': 26161}, {'type': 'llm_formatting', 'model': 'gpt-4o', 'input_tokens': 25699, 'output_tokens': 290, 'total_tokens': 25989}, {'type': 'embeddings', 'model': 'text-embedding-3-large', 'input_tokens': 95215, 'output_tokens': 0, 'total_tokens': 95215}, {'type': 'embeddings', 'model': 'text-embedding-3-large', 'input_tokens': 89094, 'output_tokens': 0, 'total_tokens': 89094}, {'type': 'document_ai', 'pages': 1}, {'type': 'document_ai', 'pages': 1}, {'type': 'document_ai', 'pages': 1}, {'type': 'document_ai', 'pages': 1}, {'type': 'document_ai', 'pages': 1}, {'type': 'document_ai', 'pages': 1}, {'type': 'document_ai', 'pages': 1}, {'type': 'document_ai', 'pages': 1}, {'type': 'document_ai', 'pages': 1}, {'type': 'document_ai', 'pages': 1}, {'type': 'document_ai', 'pages': 1}, {'type': 'document_ai', 'pages': 1}, {'type': 'document_ai', 'pages': 1}, {'type': 'document_ai', 'pages': 1}, {'type': 'document_ai', 'pages': 1}] ================================================== **Elapsed Time: 14.78 seconds** ==================================================