tag:crieit.net,2005:https://crieit.net/users/frodo821/feed frodo821の投稿 - Crieit Crieitでユーザーfrodo821による最近の投稿 2019-10-28T00:15:55+09:00 https://crieit.net/users/frodo821/feed tag:crieit.net,2005:PublicArticle/15512 2019-10-27T23:39:10+09:00 2019-10-28T00:15:55+09:00 https://crieit.net/posts/The-Zen-of-Python The Zen of Python 邦訳 <h2 id="The Zen of Python"><a href="#The+Zen+of+Python">The Zen of Python</a></h2> <p>って知ってますか?</p> <p>知らないっていう人は、Pythonインタプリタを立ち上げて、<code>import this</code>しましょう。</p> <blockquote> <p>The Zen of Python, by Tim Peters</p> <p>Beautiful is better than ugly.<br /> Explicit is better than implicit.<br /> Simple is better than complex.<br /> Complex is better than complicated.<br /> Flat is better than nested.<br /> Sparse is better than dense.<br /> Readability counts.<br /> Special cases aren't special enough to break the rules.<br /> Although practicality beats purity.<br /> Errors should never pass silently.<br /> Unless explicitly silenced.<br /> In the face of ambiguity, refuse the temptation to guess.<br /> There should be one-- and preferably only one --obvious way to do it.<br /> Although that way may not be obvious at first unless you're Dutch.<br /> Now is better than never.<br /> Although never is often better than <em>right</em> now.<br /> If the implementation is hard to explain, it's a bad idea.<br /> If the implementation is easy to explain, it may be a good idea.<br /> Namespaces are one honking great idea -- let's do more of those!</p> </blockquote> <p>こんな感じのやつ。<br /> 何番煎じかわからないけれど、邦訳してみます。</p> <h2 id="The Zen of Python, by Tim Peters 日本語訳版"><a href="#The+Zen+of+Python%2C+by+Tim+Peters+%E6%97%A5%E6%9C%AC%E8%AA%9E%E8%A8%B3%E7%89%88">The Zen of Python, by Tim Peters 日本語訳版</a></h2> <ol> <li>醜いよりは美しい方がいい。</li> <li>暗黙よりは明示の方がいい。</li> <li>複雑よりも単純な方がいい。それでも、ワケわからなくなるよか複雑な方ががマシ。</li> <li>ネストするよりフラットな方がいい。</li> <li>詰め込んであるよりも空白が多い方がいい。</li> <li>読みやすさは重要だ。</li> <li>特別な場合であっても、ルールを破るに足る特別な理由にはならない。しかしながら、純粋さよりは実用性の方が大事。</li> <li>エラーを無視してはいけない。明示的に握り潰すなら別だけれども。</li> <li>曖昧さを前にして、推測で済まそうとしてはいけない。</li> <li>あることをする明白な方法が1つあり、しかも1つしかないのが好ましい。あなたがオランダ人でもない限り、はじめは明白には思えないにしても。</li> <li>やらないよりは今やった方がいい。とはいえ、やらない方が今すぐやるよりもいいことはしばしばある。</li> <li>実装を説明するのが難しいなら、それは悪いアイデアだろう。もし、実装を説明するのが簡単なら、それは良いアイデアかもしれない。</li> <li>名前空間は1つのとてもいい考え。そういうのをもっとやろう!</li> </ol> <p>ゆるっとしたコーディングや設計に関する指針で、結構ためになります。The Zen of Pythonという名前ですが、Python以外でもこの考え方は通用します。</p> <p>初心を忘れないためにも、時々<code>import this</code>するのはいいアイデアなのではないでしょうか?</p> <h2 id="そういえば"><a href="#%E3%81%9D%E3%81%86%E3%81%84%E3%81%88%E3%81%B0">そういえば</a></h2> <p><code>this.s</code>という変数にこの文章が入っています。</p> <pre><code class="python">s = """Gur Mra bs Clguba, ol Gvz Crgref Ornhgvshy vf orggre guna htyl. Rkcyvpvg vf orggre guna vzcyvpvg. Fvzcyr vf orggre guna pbzcyrk. Pbzcyrk vf orggre guna pbzcyvpngrq. Syng vf orggre guna arfgrq. Fcnefr vf orggre guna qrafr. Ernqnovyvgl pbhagf. Fcrpvny pnfrf nera'g fcrpvny rabhtu gb oernx gur ehyrf. Nygubhtu cenpgvpnyvgl orngf chevgl. Reebef fubhyq arire cnff fvyragyl. Hayrff rkcyvpvgyl fvyraprq. Va gur snpr bs nzovthvgl, ershfr gur grzcgngvba gb thrff. Gurer fubhyq or bar-- naq cersrenoyl bayl bar --boivbhf jnl gb qb vg. Nygubhtu gung jnl znl abg or boivbhf ng svefg hayrff lbh'er Qhgpu. Abj vf orggre guna arire. Nygubhtu arire vf bsgra orggre guna *evtug* abj. Vs gur vzcyrzragngvba vf uneq gb rkcynva, vg'f n onq vqrn. Vs gur vzcyrzragngvba vf rnfl gb rkcynva, vg znl or n tbbq vqrn. Anzrfcnprf ner bar ubaxvat terng vqrn -- yrg'f qb zber bs gubfr!""" </code></pre> <p>いわゆる換字式暗号ってやつですね。換字表は<code>this.d</code>で得られます。</p> <pre><code class="python">d = {'A': 'N', 'B': 'O', 'C': 'P', 'D': 'Q', 'E': 'R', 'F': 'S', 'G': 'T', 'H': 'U', 'I': 'V', 'J': 'W', 'K': 'X', 'L': 'Y', 'M': 'Z', 'N': 'A', 'O': 'B', 'P': 'C', 'Q': 'D', 'R': 'E', 'S': 'F', 'T': 'G', 'U': 'H', 'V': 'I', 'W': 'J', 'X': 'K', 'Y': 'L', 'Z': 'M', 'a': 'n', 'b': 'o', 'c': 'p', 'd': 'q', 'e': 'r', 'f': 's', 'g': 't', 'h': 'u', 'i': 'v', 'j': 'w', 'k': 'x', 'l': 'y', 'm': 'z', 'n': 'a', 'o': 'b', 'p': 'c', 'q': 'd', 'r': 'e', 's': 'f', 't': 'g', 'u': 'h', 'v': 'i', 'w': 'j', 'x': 'k', 'y': 'l', 'z': 'm'} </code></pre> <p>そこで、この暗号を復号するプログラムをPythonで書いてみてください。<br /> <code>sys.stdout</code>を<code>io.StringIO</code>とかに差し替えてから<code>import this</code>は復号プログラムを書いたとは言いませんよ。</p> <h3 id="正答例"><a href="#%E6%AD%A3%E7%AD%94%E4%BE%8B">正答例</a></h3> <pre><code class="python">print(''.join(map(lambda x: this.d.get(x, x), this.s))) </code></pre> frodo821 tag:crieit.net,2005:PublicArticle/15509 2019-10-26T22:52:42+09:00 2019-10-27T14:20:12+09:00 https://crieit.net/posts/CLI CLIツールを簡単に作れるフレームワークを作ったよ! <p>PythonでCLIツールを簡単に作れるようになるフレームワークを作ってみました!</p> <p>argparseって正直使いにくくないですか?<br /> groomなら、実質関数を1つ定義するだけでコマンドラインツールを作成できます。</p> <h2 id="インストール"><a href="#%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB">インストール</a></h2> <p>pypiに登録してあるので簡単!</p> <pre><code class="sh">$ pip install groom </code></pre> <p>これで終わりです。早速使ってみましょう!</p> <h2 id="簡単に使ってみる"><a href="#%E7%B0%A1%E5%8D%98%E3%81%AB%E4%BD%BF%E3%81%A3%E3%81%A6%E3%81%BF%E3%82%8B">簡単に使ってみる</a></h2> <p>さて、早速使ってみましょう。<br /> 2つの数を取って、四則演算するコマンドラインツールを作ってみます。</p> <pre><code class="python">import sys from groom import positional, optional, Dispatcher __version__ = '1.0' def calculate( num1: positional(float, "former number", required=True, var_name='N1'), num2: positional(float, "latter number", required=True, var_name='N2'), operator: optional(str, "operator name", short_name='op')='add'): if operator == 'add': print(num1 + num2) return if operator == 'sub': print(num1 - num2) return if operator == 'mul': print(num1 * num2) return if operator == 'div': print(num1 / num2) return print("unknown operator:", operator, file=sys.stderr) if __name__ == '__main__': Dispatcher( calculate, "calculate one of four arithmetic operations" ).dispatch() </code></pre> <p>これだけで、ヘルプの表示やコマンドライン引数の処理までができます。<br /> 生成されたヘルプメッセージはこんな感じ。</p> <pre><code class="text">calc.py: 1.0 calculate one of four arithmetic operations Usage: calc.py [-v | --version | -h | --help] calc.py params... positional parameters: N1: former number type: float required: True N1: latter number type: float required: True parameters: --operator, -op: operator name type: str required: False multiple values: False default: add </code></pre> <p>どうでしょうか? かなり簡単になっているのではないかと思います。<br /> ヘルプメッセージの改善に関しては鋭意取り組み中ですので、少々お待ち頂けたらw</p> <hr /> <p>追記 2019/10/27</p> <p>groom==0.0.3a1で、ヘルプメッセージを少し改善しました。現在はこのようになります。</p> <pre><code class="text">calc.py: 1.0 calculate one of four arithmetic operations Usage: calc.py [-v | --version | -h | --help] calc.py <N1> <N2> [--operator <OPERATOR> | -op <OPERATOR>] positional parameters: N1: former number type: float required: True N2: latter number type: float required: True parameters: --operator, -op: operator name type: str required: False multiple values: False default: add </code></pre> <hr /> <p>一応サブコマンドにも対応していまして、以下のようにすれば可能です。が、こちらも鋭意改善中です。</p> <pre><code class="python">d = Dispatcher(func, "desc") d.add_subcommand( "sub-command", Dispatcher(subfunc, "subdesc")) </code></pre> <p>このプロジェクトのGitHubのリポジトリは<a target="_blank" rel="nofollow noopener" href="https://github.com/frodo821/groom">https://github.com/frodo821/groom</a>です。issueやpull requestなど、どんどんお寄せいただけたら凄く嬉しいです。よろしくお願いします。</p> frodo821 tag:crieit.net,2005:PublicArticle/15387 2019-09-10T20:49:01+09:00 2019-09-10T20:49:01+09:00 https://crieit.net/posts/TSV-Markdown TSVで定義した辞書をMarkdownで出力するツールを作った話 <h2 id="TL;DR"><a href="#TL%3BDR">TL;DR</a></h2> <p>電子辞書が欲しくなったので作ることにしました。今回の要件は、品詞別の索引と全単語の索引、先頭の文字ごとの詳細解説があることです。</p> <p>そこで辞書本体をmarkdownで書くことにしたのですが、ちまちま手で書くのは面倒くさい。なのでTSVを読み込んでmarkdownを吐くジェネレータを簡単に書いてみることにしました。</p> <h2 id="TSVに格納する辞書の形式を考えてみた"><a href="#TSV%E3%81%AB%E6%A0%BC%E7%B4%8D%E3%81%99%E3%82%8B%E8%BE%9E%E6%9B%B8%E3%81%AE%E5%BD%A2%E5%BC%8F%E3%82%92%E8%80%83%E3%81%88%E3%81%A6%E3%81%BF%E3%81%9F">TSVに格納する辞書の形式を考えてみた</a></h2> <p>TSVとは言いつつ、純粋なTSVは使っていません。まずは辞書のヘッダ部分です。</p> <pre><code>BEGIN_HEADER LANGUAGE_LONG Language Name LANGUAGE_CODE LC(注1) PHONETICAL_CHARS 頭文字になりうる文字の列挙(注2) END_HEADER </code></pre> <ul> <li>注1 これは2~3文字の言語コードです。<code>ja</code>, <code>en</code>など</li> <li>注2 スペース区切りで列挙します。<code>a b c d e f g h i j k l m n o p q r s t u v w x y z</code>のように</li> </ul> <p>続いて、辞書の本体を考えてみました。</p> <pre><code>BEGIN_DICTIONARY 単語 品詞ID(注1) 意味 関連語(注2) END_DICTIONARY </code></pre> <ul> <li>注1 品詞IDは任意の文字列です。</li> <li>注2 関連語はスペース区切りで列挙します。<code>study learn</code>のように</li> <li>単語は任意個この形式で列挙します。</li> </ul> <p>最後に、品詞の定義です。</p> <pre><code>BEGIN_DEFINITION 品詞ID 品詞の名称 END_DEFINITION </code></pre> <p>このフィールドでは、DICTIONARYフィールド内で使用した品詞IDとその名称の対応(<code>NOUN</code>と名詞のような)を定義します。</p> <h2 id="パーサーをざっくり書いてみる"><a href="#%E3%83%91%E3%83%BC%E3%82%B5%E3%83%BC%E3%82%92%E3%81%96%E3%81%A3%E3%81%8F%E3%82%8A%E6%9B%B8%E3%81%84%E3%81%A6%E3%81%BF%E3%82%8B">パーサーをざっくり書いてみる</a></h2> <p>さて、このパーサーをざっくり書いてみました。</p> <pre><code class="python">import csv class ParseError(SyntaxError): pass def open_dict(dic_path: str) -> list: with open(dic_path, encoding='utf-8') as f: reader = csv.reader(f, delimiter='\t') return list(reader) def parse_dict(dic: list) -> dict: ret = {} state = 'none' for i in dic: if i[0] == 'BEGIN_HEADER': if state != 'none': raise ParseError('Unexpected BEGIN_HEADER tag.') state = 'header' ret['header'] = {} continue if i[0] == 'END_HEADER': if state != 'header': raise ParseError('Unexpected END_HEADER tag.') state = 'none' continue if i[0] == 'BEGIN_DICTIONARY': if state != 'none': raise ParseError('Unexpected BEGIN_DICTIONARY tag.') state = 'dictionary' ret['dict'] = {} continue if i[0] == 'END_DICTIONARY': if state != 'dictionary': raise ParseError('Unexpected END_DICTIONARY tag.') state = 'none' continue if i[0] == 'BEGIN_DEFINITION': if state != 'none': raise ParseError('Unexpected BEGIN_DEFINITION tag.') state = 'definition' ret['defs'] = {} continue if i[0] == 'END_DEFINITION': if state != 'definition': raise ParseError('Unexpected END_DEFINITION tag.') state = 'none' continue if state == 'none': continue if state == 'header': ret['header'][i[0]] = i[1] continue if state == 'dictionary': if i[0] not in ret['dict']: ret['dict'][i[0]] = {} ret['dict'][i[0]][i[1]] = { 'meaning': i[2], 'reference': i[3].split(' ') } continue if state == 'definition': ret['defs'][i[0]] = i[1] if state != 'none': raise ParseError(f'A match pair tag of END_{state.upper()} not found.') return ret </code></pre> <p>まぁ、本当に簡単に書いているので、解説することもほとんどないんですけれど……。ざっくり説明すると、tsvを読み込んで2次元配列に格納し、それを先ほど定義したフォーマットに従って辞書に格納しなおしているだけです。</p> <p>次に、この生成した辞書から索引情報を抽出する関数を定義してみます。</p> <pre><code class="python">def get_comparator(_order): class _Comparator(str): def __gt__(self, other): order = list(_order) for s, o in zip(self, other): oi = order.index(o) si = order.index(s) if oi > si: return True if si > oi: return False return len(self) > len(other) def __lt__(self, other): order = list(_order) for s, o in zip(self, other): oi = order.index(o) si = order.index(s) if oi < si: return True if si < oi: return False return len(self) < len(other) return _Comparator def generate_indices(dic: dict): chars = dic['header'].get( 'PHONETICAL_CHARS', 'a b c d e f g h i j k l m n o p q r s t u v w x y z').split(' ') nodes = {i: {c: [] for c in chars} for i in dic['defs']} nodes['ALPHABETICAL'] = {c: [] for c in chars} comp = get_comparator(''.join(chars)) for word, data in dic['dict'].items(): nodes['ALPHABETICAL'][word[0]].append(word) for kind in data: nodes[kind][word[0]].append(word) for i in nodes.values(): for j in i.values(): j.sort(key=comp) return nodes </code></pre> <p><code>get_comparator</code>関数は、<code>PHONETICAL_CHARS</code>ヘッダフィールドで定義された辞書順に従って文字列を比較できるようにするラッパークラスを返す関数です。品詞ごとに単語の頭の文字の配列を作り、そこに単語のみを格納しているだけですね。<code>ALPHABETICAL</code>は全単語索引の情報で、定義されたすべての単語が登録されています。</p> <p>次に意味情報と関連語情報を抽出する関数を定義します。</p> <pre><code class="python">def generate_dict_content(dic: dict): chars = dic['header'].get( 'PHONETICAL_CHARS', 'a b c d e f g h i j k l m n o p q r s t u v w x y z').split(' ') defs = dic['defs'] contents = {i: [] for i in chars} comp = get_comparator(''.join(chars)) for word, data in dic['dict'].items(): contents[word[0]].append({ 'surface': word, 'meaning': [ (kind, meta['meaning'].split(' ')) for kind, meta in data.items() ], 'reference': [ref for d in data.values() for ref in d['reference']] }) for c in contents.values(): c.sort(key=lambda x: comp(x['surface'])) return contents </code></pre> <p>この関数についての解説は、<code>generate_indices</code>関数とほぼ同じ動作のため割愛します。</p> <p>これでTSVから単語情報を抽出する関数がそろいました。次はこれをmarkdownとして出力する関数を作っていきます。</p> <h2 id="抽出した情報をMarkdownにしてみる"><a href="#%E6%8A%BD%E5%87%BA%E3%81%97%E3%81%9F%E6%83%85%E5%A0%B1%E3%82%92Markdown%E3%81%AB%E3%81%97%E3%81%A6%E3%81%BF%E3%82%8B">抽出した情報をMarkdownにしてみる</a></h2> <p>索引を生成する関数を考えてみました。こんな感じです。</p> <pre><code class="python">def generate_index_file(kind: str, defs: dict, index: list): ret = f"# {defs[kind]}\n" if kind == 'ALPHABETICAL': ret += "\n## 品詞別インデックス\n" for fp, kd in defs.items(): if fp != 'ALPHABETICAL': ret += f"* [{kd}](./{fp.lower()}.md)\n" for representative, content in index.items(): ret += f"\n## {representative.upper()}\n" for word in content: ret += f"* [{word}](./content/{word[0].upper()}.md#{word})\n" return ret </code></pre> <p>すごいシンプルにかけて満足しています。あまりPythnoicではないと思いますが、そこは気にしないことにします。あと、<code>ALPHABETICAL</code>のページに品詞別インデックスへのリンクを表示することにしました。引数の意味ですが、<code>kind</code>は品詞ID、<code>defs</code>は品詞の定義、<code>index</code>には単語のリストを渡します。</p> <p>続いて、単語の解説ページを生成する関数を考えてみました。</p> <pre><code class="python">def generate_content_file(representative: str, words: list, defs: dict): ret = f"# {representative.upper()}\n" for word in words: ret += f"\n## {word['surface']}\n" ret += "意味: \n" for i, (k, m) in enumerate(word['meaning']): ret += f"{i + 1}. <{defs[k]}> \n" for ml in m: ret += f" {ml} \n" refs = [i for i in word['reference'] if i] if refs: ret += "\n関連語: \n" for ref in refs: ret += f"* [{ref}](./{ref[0].upper()}.md#{ref})\n" return ret </code></pre> <p>引数の意味ですが、<code>representative</code>は代表の文字(ようするにそのページの単語に共通の頭文字)、<code>words</code>は単語とそのメタ情報、<code>defs</code>は品詞の定義をとります。</p> <p>さて、最後にこれらの関数の動作を連結する関数を書きましょう。それで完成です。</p> <pre><code class="python">def generate_markdown_files(dic: dict): indices = generate_indices(dic) content = generate_dict_content(dic) chars = dic['header'].get( 'PHONETICAL_CHARS', 'a b c d e f g h i j k l m n o p q r s t u v w x y z').split(' ') defs = dic['defs'].copy() defs['ALPHABETICAL'] = "全単語索引" return { 'content': { i: generate_content_file(i, content[i], defs) for i in chars }, 'indices': { ('index' if i == 'ALPHABETICAL' else i.lower()): generate_index_file(i, defs, content) for i, content in indices.items() } } </code></pre> <p>この関数はparseされたTSVをmarkdown形式の文字列に変換する関数です。ここまでに定義した関数を連結して整形された形にするのが役割ですね。</p> <p>さて、これをファイルにdumpする関数を書いて、それで本当に完成です。</p> <pre><code class="python">def dump_markdown(dic_path: str, dump_dir: dir): dic_path = abspath(dic_path) dump_dir = abspath(dump_dir) files = generate_markdown_files(parse_dict(open_dict(dic_path))) print('generating indices...') for rep, con in files['indices'].items(): path = join(dump_dir, f'{rep}.md') d = dirname(path) if not exists(d): md(d) print(f'writing file: {path}') with open(path, 'w', encoding='utf-8') as f: f.write(con) print('generating content...') for rep, con in files['content'].items(): path = join(dump_dir, 'content', f'{rep.upper()}.md') d = dirname(path) if not exists(d): md(d) print(f'writing file: {path}') with open(path, 'w', encoding='utf-8') as f: f.write(con) print('done.') </code></pre> <p>この関数は、コンソールコマンドとして実行されることを想定したものになっています。</p> <p>最後に、ここまで書いたスクリプトの全体を示しておきます。</p> <pre><code class="python">#-*- coding: utf-8;-*- from os import makedirs as md from os.path import join, exists, dirname, abspath import csv class ParseError(SyntaxError): pass def get_comparator(_order): class _Comparator(str): def __gt__(self, other): order = list(_order) for s, o in zip(self, other): oi = order.index(o) si = order.index(s) if oi > si: return True if si > oi: return False return len(self) > len(other) def __lt__(self, other): order = list(_order) for s, o in zip(self, other): oi = order.index(o) si = order.index(s) if oi < si: return True if si < oi: return False return len(self) < len(other) return _Comparator def open_dict(dic_path: str) -> list: with open(dic_path, encoding='utf-8') as f: reader = csv.reader(f, delimiter='\t') return list(reader) def parse_dict(dic: list) -> dict: ret = {} state = 'none' for i in dic: if i[0] == 'BEGIN_HEADER': if state != 'none': raise ParseError('Unexpected BEGIN_HEADER tag.') state = 'header' ret['header'] = {} continue if i[0] == 'END_HEADER': if state != 'header': raise ParseError('Unexpected END_HEADER tag.') state = 'none' continue if i[0] == 'BEGIN_DICTIONARY': if state != 'none': raise ParseError('Unexpected BEGIN_DICTIONARY tag.') state = 'dictionary' ret['dict'] = {} continue if i[0] == 'END_DICTIONARY': if state != 'dictionary': raise ParseError('Unexpected END_DICTIONARY tag.') state = 'none' continue if i[0] == 'BEGIN_DEFINITION': if state != 'none': raise ParseError('Unexpected BEGIN_DEFINITION tag.') state = 'definition' ret['defs'] = {} continue if i[0] == 'END_DEFINITION': if state != 'definition': raise ParseError('Unexpected END_DEFINITION tag.') state = 'none' continue if state == 'none': continue if state == 'header': ret['header'][i[0]] = i[1] continue if state == 'dictionary': if i[0] not in ret['dict']: ret['dict'][i[0]] = {} ret['dict'][i[0]][i[1]] = { 'meaning': i[2], 'reference': i[3].split(' ') } continue if state == 'definition': ret['defs'][i[0]] = i[1] if state != 'none': raise ParseError(f'A match pair tag of END_{state.upper()} not found.') return ret def generate_indices(dic: dict): chars = dic['header'].get( 'PHONETICAL_CHARS', 'a b c d e f g h i j k l m n o p q r s t u v w x y z').split(' ') nodes = {i: {c: [] for c in chars} for i in dic['defs']} nodes['ALPHABETICAL'] = {c: [] for c in chars} comp = get_comparator(''.join(chars)) for word, data in dic['dict'].items(): nodes['ALPHABETICAL'][word[0]].append(word) for kind in data: nodes[kind][word[0]].append(word) for i in nodes.values(): for j in i.values(): j.sort(key=comp) return nodes def generate_dict_content(dic: dict): chars = dic['header'].get( 'PHONETICAL_CHARS', 'a b c d e f g h i j k l m n o p q r s t u v w x y z').split(' ') defs = dic['defs'] contents = {i: [] for i in chars} comp = get_comparator(''.join(chars)) for word, data in dic['dict'].items(): contents[word[0]].append({ 'surface': word, 'meaning': [ (kind, meta['meaning'].split(' ')) for kind, meta in data.items() ], 'reference': [ref for d in data.values() for ref in d['reference']] }) for c in contents.values(): c.sort(key=lambda x: comp(x['surface'])) return contents def generate_content_file(representative: str, words: list, defs: dict): ret = f"# {representative.upper()}\n" for word in words: ret += f"\n## {word['surface']}\n" ret += "意味: \n" for i, (k, m) in enumerate(word['meaning']): ret += f"{i + 1}. <{defs[k]}> \n" for ml in m: ret += f" {ml} \n" refs = [i for i in word['reference'] if i] if refs: ret += "\n関連語: \n" for ref in refs: ret += f"* [{ref}](./{ref[0].upper()}.md#{ref})\n" return ret def generate_index_file(kind: str, defs: dict, index: list): ret = f"# {defs[kind]}\n" if kind == 'ALPHABETICAL': ret += "\n## 品詞別インデックス\n" for fp, kd in defs.items(): if fp != 'ALPHABETICAL': ret += f"* [{kd}](./{fp.lower()}.md)\n" for representative, content in index.items(): ret += f"\n## {representative.upper()}\n" for word in content: ret += f"* [{word}](./content/{word[0].upper()}.md#{word})\n" return ret def generate_markdown_files(dic: dict): indices = generate_indices(dic) content = generate_dict_content(dic) chars = dic['header'].get( 'PHONETICAL_CHARS', 'a b c d e f g h i j k l m n o p q r s t u v w x y z').split(' ') defs = dic['defs'].copy() defs['ALPHABETICAL'] = "全単語索引" return { 'content': { i: generate_content_file(i, content[i], defs) for i in chars }, 'indices': { ('index' if i == 'ALPHABETICAL' else i.lower()): generate_index_file(i, defs, content) for i, content in indices.items() } } def dump_markdown(dic_path: str, dump_dir: dir): dic_path = abspath(dic_path) dump_dir = abspath(dump_dir) files = generate_markdown_files(parse_dict(open_dict(dic_path))) print('generating indices...') for rep, con in files['indices'].items(): path = join(dump_dir, f'{rep}.md') d = dirname(path) if not exists(d): md(d) print(f'writing file: {path}') with open(path, 'w', encoding='utf-8') as f: f.write(con) print('generating content...') for rep, con in files['content'].items(): path = join(dump_dir, 'content', f'{rep.upper()}.md') d = dirname(path) if not exists(d): md(d) print(f'writing file: {path}') with open(path, 'w', encoding='utf-8') as f: f.write(con) print('done.') if __name__ == '__main__': from sys import argv dump_markdown(argv[1], argv[2]) </code></pre> <p>はー。疲れました。はい、これでおそらくどの方面にも需要がないツールの完成です。「欲しかったから作った」の真骨頂ですね。最後までお付き合いいただき、ありがとうございました。</p> frodo821 tag:crieit.net,2005:PublicArticle/15338 2019-08-20T17:41:42+09:00 2019-08-20T17:41:42+09:00 https://crieit.net/posts/HTML-JSX HTMLのノードを直接生成するJSXファクトリを簡単に書いてみた <p>JSXって知っていますか? JSXとは、Reactなどに使われているXML(HTML)とjavascriptの混在記法です。<br /> そして、JSXを内部的に処理する関数をJSXファクトリといいます。<br /> これは、BabelやTypeScriptコンパイラの設定で自由に変更可能です。<br /> ところでWeb Componentsという、新しいHTMLエレメントを自由に作れるjavascriptのAPIが存在するのはご存じでしょうか。</p> <p>これを使えば、Reactのようなことがネイティブのjavascriptでできるのではないだろうか?そう考えたのでやってみることにしました。</p> <p>まずはJSXファクトリの仕様から説明します。<br /> JSXファクトリは以下のようなシグネチャを持つ関数であれば何でも構いません。</p> <pre><code class="javascript">/** * @param element 文字列または関数またはクラス。文字列の場合はタグの名前、クラス・関数の場合はタグが表すコンポーネント。 * @param props 要素の属性。オブジェクトで渡す。存在しない場合はnull。 * @param children 可変長引数で、現在の要素の子要素を渡す。 */ function JSXFactory(element, props, ...children) {} </code></pre> <p>そしてこれは重要なことなのですが、この関数が返す値や動作は何でもよいのです。<br /> JSXとは、単なる糖衣構文でしかありません。つまり、その動作は実装した人にゆだねられます。<br /> 以下に、JSXの変換例を示します。</p> <pre><code class="jsx">import React from 'react'; let node = <div className="hoge">This is child text.</div>; </code></pre> <p>これは以下のようなjavascriptに変換されます。</p> <pre><code class="javascript">import React from 'react'; let node = React.createElement('div', {'className': 'hoge'}, 'This is child text'); </code></pre> <p>そしてこの、React.createElementにあたる関数はtsconfig.json内で以下のように設定できます。</p> <pre><code class="json">{ "compilerOptions": { "jsx": "react", "jsxFactory": "myCreateElement" } } </code></pre> <p>この場合はjsxFactoryにmyCreateElementを設定しているため、React.createElement関数の代わりにmyCreateElementが使われます。<br /> さて、これらの仕様を使って、普通のHTMLノードを生成するJSXファクトリを作ってみたいと思います。<br /> まずは型の宣言から。</p> <pre><code class="typescript">declare namespace JSX { export interface JSXElement<P = {}> {} export interface IntrinsicElements { [elemName: string]: any; } export type GlobalAttributes = EventAttributes | ElementAttributes; export type ElementAttributes = /* HTMLノードのグローバル属性(略) */ export type EventAttributes = /* イベント属性(略) */ } </code></pre> <p>シンプルに行きましょう。<br /> 組み込みの要素の型をいちいち定義していたら日が暮れます。<br /> なのでanyで誤魔化しました。</p> <p>続いては本題のJSXファクトリの実装です。</p> <pre><code class="typescript">import './types'; type PropType<K> = { [P in JSX.GlobalAttributes | keyof K]: any }; export function createElement<K, E extends JSX.JSXElement<K>>( element: E | string, props: PropType<K>, ...children: any[] ): Element { let elem: Element; if (typeof element === 'string') { elem = document.createElement(element); } else { elem = document.createElement( (element as any).name.replace(/(?!^)([A-Z0-9])/g, '-$1').toLowerCase() ); } Object.keys(props || {}) .filter(it => props.hasOwnProperty(it)) .forEach(it => elem.setAttribute(convertPropName(it), props[it as keyof PropType<K>]) ); children.forEach(it => !(it instanceof Element) ? it.hasOwnProperty('render') && typeof it.render === 'function' ? elem.appendChild(it.render()) : elem.append(it.toString()) : elem.appendChild(it) ); return elem; } function convertPropName(name: string): string { switch (name) { case 'className': return 'class'; case 'htmlFor': return 'for'; default: return name; } } </code></pre> <p>やってること自体はきわめて単純。<br /> <code>document.createElement</code>関数でノードを作り、引数に従って属性や子要素を追加しているだけです。<br /> JSXではclassとforがそれぞれclassNameとhtmlForになるため、convertPropName関数はそれを変換します。</p> <p>にしてもJSXファクトリ、かなり面白いですね。<br /> これ使えばいろいろできそうな予感。<br /> React以外でも活用の目はありそうです。</p> frodo821 tag:crieit.net,2005:PublicArticle/15327 2019-08-16T15:05:02+09:00 2019-08-16T15:06:16+09:00 https://crieit.net/posts/CSS-Property-Value-Selector-50 俺的CSS便利Property・Value・Selector 50連発! <p>最近すごくHTMLやCSSを書いている気がするので、ここらでCSSのよく使うプロパティや知っていると便利なプロパティをまとめてみることにしました。早速行ってみましょう!</p> <h1 id="よく使うもの"><a href="#%E3%82%88%E3%81%8F%E4%BD%BF%E3%81%86%E3%82%82%E3%81%AE">よく使うもの</a></h1> <h2 id="1. ::before, ::after"><a href="#1.+%3A%3Abefore%2C+%3A%3Aafter">1. ::before, ::after</a></h2> <p>これを指定した要素の子要素として挿入される疑似要素。子要素を持てない要素(img, inputなど)には指定不可能。<code>content</code>プロパティを指定しない限り挿入されない。なにも入れるcontentがない場合は<code>content: ""</code>を指定する。</p> <h2 id="2. min-height, min-width"><a href="#2.+min-height%2C+min-width">2. min-height, min-width</a></h2> <p>高さや幅の最小値を指定する。mainコンテナのように、必ずある大きさ以上の大きさが必要な要素に指定すると、コンテンツの大きさにかかわらず設定した大きさより小さくならない。</p> <h2 id="3. max-height, max-width"><a href="#3.+max-height%2C+max-width">3. max-height, max-width</a></h2> <p>上の<code>min-</code>系とは逆に、高さや幅の最大値を設定する。指定すると、コンテンツの大きさにかかわらず、設定した大きさより大きくならない。</p> <h2 id="4. order"><a href="#4.+order">4. order</a></h2> <p>HTMLの要素の兄弟順をオーバーライドする。これを指定すると、強制的に兄弟要素の中でその順番になるように表示される。<code>nth-</code>系には影響しない。</p> <h2 id="5. visibility"><a href="#5.+visibility">5. visibility</a></h2> <p><code>display: none</code>と違って、表示したくないけれど領域は確保したいという場合に使える。</p> <h2 id="6. z-index"><a href="#6.+z-index">6. z-index</a></h2> <p>要素の描画順を設定するプロパティ。<code>position: fixed;</code>と使うことが多い。</p> <h2 id="7. clip-path"><a href="#7.+clip-path">7. clip-path</a></h2> <p>要素の見かけの外形を設定するプロパティ。うまく使えばCSSだけでいろんな表現が可能に。</p> <h2 id="8. will-change"><a href="#8.+will-change">8. will-change</a></h2> <p>要素のCSSプロパティがアニメーションで変化することをあらかじめブラウザに知らせる。パフォーマンス爆上がり。</p> <h2 id="9. transform"><a href="#9.+transform">9. transform</a></h2> <p>要素を移動、回転、拡縮、変形させるプロパティ。ちょっと場所がずれるときとか、アニメーションで動かしたいときに便利。</p> <h2 id="10. :root"><a href="#10.+%3Aroot">10. :root</a></h2> <p>root疑似クラス。document.documentElementを指し示すセレクタ。カスタムプロパティと組み合わせると応用範囲は無限大。メディアクエリと組み合わせるとモバイルビュー判定にも使える。</p> <pre><code class="css">@media screen and (max-width: 960px) { :root { --is-mobile: yes; } } @media screen and (min-width: 961px) { :root { --is-mobile: no; } } </code></pre> <h2 id="11. transform-origin"><a href="#11.+transform-origin">11. transform-origin</a></h2> <p>transformプロパティの基準になる点を指定するプロパティ。ローディング画面のスピナなどを作るときなど、意外と使える場面が多い。</p> <h2 id="12. position: relative"><a href="#12.+position%3A+relative">12. position: relative</a></h2> <p><code>position: absolute</code>が親要素を基準とした位置取りにならない!?って思ったときは大概こいつの設定忘れが原因。これが設定された要素の子要素に<code>position: absolute</code>が指定された場合、これが設定された要素を基準とした位置取りになる。</p> <h2 id="13. appearance: none"><a href="#13.+appearance%3A+none">13. appearance: none</a></h2> <p>デフォルトの見た目をなくすプロパティ。input要素に独自のスタイルを当てたいときなど、これを設定するとうまくいくことが多い。</p> <h2 id="14. box-shadow"><a href="#14.+box-shadow">14. box-shadow</a></h2> <p>要素が落とす影を設定するプロパティ。背景が設定されていない要素につけると違和感マシマシ。そういうインラインなテキスト要素に影をつけたい場合は、<code>text-shadow</code>プロパティを使うこと。</p> <h2 id="15. background-clip"><a href="#15.+background-clip">15. background-clip</a></h2> <p>きれいなタイトル文字の修飾をしたいときに。ヘッディング要素の<code>background-image</code>プロパティに適当な画像を設定して、このプロパティの値を<code>text</code>にすると、背景が文字の形で切り抜かれる。</p> <h2 id="16. background-attachment: fixed"><a href="#16.+background-attachment%3A+fixed">16. background-attachment: fixed</a></h2> <p>要素内のコンテンツが動いても、背景を動かしたくない場合に使う。サイトの背景に設定するといい感じになるかも。</p> <h2 id="17. border-radius"><a href="#17.+border-radius">17. border-radius</a></h2> <p>要素を角丸にできる。もはや説明不要。</p> <h2 id="18. :not"><a href="#18.+%3Anot">18. :not</a></h2> <p>セレクタにマッチしない要素にマッチする。例えば、<code>:not(.not-select)</code>というセレクタは、not-selectクラスを持たない要素にマッチする。</p> <h2 id="19. :first-child, :last-child, :nth-child, :nth-last-child"><a href="#19.+%3Afirst-child%2C+%3Alast-child%2C+%3Anth-child%2C+%3Anth-last-child">19. :first-child, :last-child, :nth-child, :nth-last-child</a></h2> <p>指定された順番にある兄弟要素にマッチする疑似クラス。</p> <h2 id="20. :first-of-type, :last-of-type, :nth-of-type, nth-last-of-type"><a href="#20.+%3Afirst-of-type%2C+%3Alast-of-type%2C+%3Anth-of-type%2C+nth-last-of-type">20. :first-of-type, :last-of-type, :nth-of-type, nth-last-of-type</a></h2> <p>指定された順番にある同じタグ(spanどうし、divどうしなど)の兄弟要素にマッチする疑似クラス。</p> <h2 id="21. :required"><a href="#21.+%3Arequired">21. :required</a></h2> <p>required属性のあるformパーツにマッチする。</p> <h2 id="22. :invalid"><a href="#22.+%3Ainvalid">22. :invalid</a></h2> <p>入力された値が要求された形式に従っていないformパーツにマッチする。</p> <h2 id="23. outline"><a href="#23.+outline">23. outline</a></h2> <p>borderの外側に描画されるborderみたいなやつ。borderと違って、それ専用に場所が確保されない。</p> <h2 id="24. attr()"><a href="#24.+attr%28%29">24. attr()</a></h2> <p>print画面でだけ<code>::after</code>疑似要素にurlを表示したいときなど、CSSのプロパティに特定のHTML属性を設定したいときに使用する。</p> <h2 id="25. user-select"><a href="#25.+user-select">25. user-select</a></h2> <p>ユーザーが要素を選択可能かどうかを設定する。spanにclickイベントを設定したときなど、選択されるのが鬱陶しかったり、要素を選択してほしくないときに使う。</p> <h2 id="26. pointer-events"><a href="#26.+pointer-events">26. pointer-events</a></h2> <p>要素のどこがマウスイベントをキャプチャするか指定するプロパティ。オーバーレイなど、ほかの要素より前に出るがマウスイベントをキャプチャしたくない要素には<code>none</code>を設定すると吉。</p> <h2 id="27. cursor"><a href="#27.+cursor">27. cursor</a></h2> <p>要素の上に乗ったカーソルの表示の種類を設定する。ユーザビリティが上がるらしい。</p> <h2 id="28. :checked"><a href="#28.+%3Achecked">28. :checked</a></h2> <p>チェックが入っているチェックボックスにマッチする。うまく活用するとHTML/CSSだけで開閉するメニューなどの2つの状態を持つUIが記述できる。</p> <h2 id="29. display: flex"><a href="#29.+display%3A+flex">29. display: flex</a></h2> <p>要素をflexboxにする。子要素の数によって、自動でいい感じに配置を決めてくれる。すごく便利。ついつい多用しがち。</p> <h2 id="30. justify-content"><a href="#30.+justify-content">30. justify-content</a></h2> <p>要素の配置を決めるプロパティ。上の<code>display: flex</code>と組み合わせて使うことが多い。</p> <h2 id="31. flex-flow"><a href="#31.+flex-flow">31. flex-flow</a></h2> <p>flexboxで、要素を配置する向きを決める。</p> <h2 id="32. flex"><a href="#32.+flex">32. flex</a></h2> <p>flexboxの子要素に指定するプロパティ。flex-grow、flex-shrink、flex-basisの3つを一気に指定できる。flex-growは要素がコンテナより小さいときにどれくらい大きくするか、flex-shrinkは逆に要素がコンテナより大きいときにどれくらい小さくするか、flex-basisは要素がコンテナの中でどれくらいの初期サイズを持っているのかを指定する。</p> <h1 id="そこまで使うわけでもないけど知っていたら便利なもの"><a href="#%E3%81%9D%E3%81%93%E3%81%BE%E3%81%A7%E4%BD%BF%E3%81%86%E3%82%8F%E3%81%91%E3%81%A7%E3%82%82%E3%81%AA%E3%81%84%E3%81%91%E3%81%A9%E7%9F%A5%E3%81%A3%E3%81%A6%E3%81%84%E3%81%9F%E3%82%89%E4%BE%BF%E5%88%A9%E3%81%AA%E3%82%82%E3%81%AE">そこまで使うわけでもないけど知っていたら便利なもの</a></h1> <h2 id="33. position: sticky"><a href="#33.+position%3A+sticky">33. position: sticky</a></h2> <p>これを設定された要素が親要素の中でrelative + fixedのような振る舞いをするようになる。Qiitaのいいねボタンやストックボタンのスマホ版みたいな振る舞いがこれ(とtop, bottom, right, leftプロパティのどれか1つ以上)だけで記述可能。</p> <h2 id="34. text-indent"><a href="#34.+text-indent">34. text-indent</a></h2> <p>行頭字下げをするプロパティ。パラグラフの先頭で字下げしたいときに有効。</p> <h2 id="35. ::first-letter"><a href="#35.+%3A%3Afirst-letter">35. ::first-letter</a></h2> <p>ある要素の中で、最初の文字にマッチする疑似要素。パラグラフの最初の文字だけ大きくするような表現に使える。</p> <h2 id="36. ::select"><a href="#36.+%3A%3Aselect">36. ::select</a></h2> <p>ある要素の中で、現在選択されているものにマッチする疑似要素。選択部分の強調などに使える。</p> <h2 id="37. ::target"><a href="#37.+%3A%3Atarget">37. ::target</a></h2> <p>urlのターゲットになっている要素にマッチする。ページ内リンクで飛んだ先の要素を強調するのに使える。</p> <h2 id="38. perspective"><a href="#38.+perspective">38. perspective</a></h2> <p>transformと組み合わせて使う。遠近法をどの程度かけるか指定するプロパティ。</p> <h2 id="39. transform-style: preserve-3d"><a href="#39.+transform-style%3A+preserve-3d">39. transform-style: preserve-3d</a></h2> <p>これを三次元の移動回転変形を指定したtransformプロパティを持つ要素の親に指定することで、3次元的な描写を可能にすることができる。</p> <h2 id="40. filter"><a href="#40.+filter">40. filter</a></h2> <p>要素に指定したSVGフィルタをかける。<code>::before</code>に指定して背景をぼかしたり、グレイスケール化したり、いろいろな視覚効果を追加することができる。</p> <h2 id="41. ::marker"><a href="#41.+%3A%3Amarker">41. ::marker</a></h2> <p>::marker<br /> リストのマーカーにマッチする疑似要素。</p> <ul> <li>←これがリストのマーカー。</li> </ul> <h2 id="42. ::placeholder"><a href="#42.+%3A%3Aplaceholder">42. ::placeholder</a></h2> <p>formパーツ要素のplaceholderにマッチする。placeholderの色やフォントなどを変えたいときに便利。</p> <h2 id="43. :defined"><a href="#43.+%3Adefined">43. :defined</a></h2> <p>任意のCustomElementにマッチする。</p> <h2 id="44. quotes"><a href="#44.+quotes">44. quotes</a></h2> <p><code><q>~</q></code>のタグを使ったときに表示される引用符を制御できる。それ以外のタグの場合も、以下のようにして利用できる。</p> <pre><code class="css">blockquote { quotes: "「" "」"; } blockquote::before { content: open-quote; } blockquote::after { content: close-quote; } </code></pre> <p>より正確な動作の説明としては「<code>open-quote</code>と<code>close-quote</code>の値を制御する」ということになる。</p> <h2 id="45. text-overflow"><a href="#45.+text-overflow">45. text-overflow</a></h2> <p>文字がコンテナのサイズを超過したときの処理を設定する。<code>...</code>のような記号で省略したり、それ以外の文字で省略したり、あるいはそれ以上を表示しなかったりするような表現が可能。</p> <h2 id="46. writing-mode"><a href="#46.+writing-mode">46. writing-mode</a></h2> <p>文字を縦書きにするか横書きにするか設定できる。サイトの種類によっては多用することもあるかも。</p> <h2 id="47. text-orientation"><a href="#47.+text-orientation">47. text-orientation</a></h2> <p><code>writing-mode</code>を<code>vertical-rl</code>または<code>vertical-lr</code>にしたときに、文字をどの向きで配置するか設定する。</p> <h2 id="48. clear"><a href="#48.+clear">48. clear</a></h2> <p>浮動要素のまわりの固定要素の配置(回り込み)を指定する。浮動要素しか含まないような要素の高さを維持するために使うこともできる。</p> <pre><code class="css">.float-only::after { content: ""; display: block; clear: both; } </code></pre> <h2 id="49. all"><a href="#49.+all">49. all</a></h2> <p>すべてのプロパティを一気に設定できる。親要素からすべてのスタイルを継承したいときや、逆に継承したくないとき、デフォルトの見た目にしたいときに使える。</p> <h2 id="50. touch-action"><a href="#50.+touch-action">50. touch-action</a></h2> <p>要素に対して可能なタッチアクションの種類を制限できる。たとえば、ズームしてほしくないとか、縦方向のスクロールを制限したいとかそういう場合に便利。Cookie Clickerのタッチ領域にこれを指定してほしいと何度思ったことか。</p> <h1 id="まとめ"><a href="#%E3%81%BE%E3%81%A8%E3%82%81">まとめ</a></h1> <p>CSS難しい……。MDNの解説ってプロパティごとに内容の質にけっこうばらつきが大きいんですね。</p> <h1 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h1> <ul> <li>https://developer.mozilla.org/en-US/docs/Web/CSS</li> <li>https://www.w3schools.com/cssref/</li> </ul> <p>Happy Hacking!</p> <p><a target="_blank" rel="nofollow noopener" href="https://www.tech-frodo.xyz/2019/08/useful-css.html">この記事はブログにも投稿しています。</a></p> frodo821 tag:crieit.net,2005:PublicArticle/15090 2019-06-11T11:13:41+09:00 2019-06-11T11:13:41+09:00 https://crieit.net/posts/Python-List Pythonで学ぶ データ構造入門 List編 <h2 id="TL;DR"><a href="#TL%3BDR">TL;DR</a></h2> <p>データ構造の基本であるList(LinkedList)やHashMap、Queue、Dequeを自分で実装して理解を深めようという趣旨でやっていきます。</p> <h2 id="まずはLinkedListを作ってみる"><a href="#%E3%81%BE%E3%81%9A%E3%81%AFLinkedList%E3%82%92%E4%BD%9C%E3%81%A3%E3%81%A6%E3%81%BF%E3%82%8B">まずはLinkedListを作ってみる</a></h2> <p>LinkedListとは、リストの各々の要素に次の要素への参照をつけておくことで一連のデータを表現できるデータ構造です。</p> <p><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209421/e496a785-37db-f4cf-444d-cdec5ba94fe2.png" alt="data structure.png" /></p> <p>このように、それぞれの要素が次への参照とデータを持っています。<br /> これをPythonで実装してみます。<br /> まずはそれぞれの要素のクラスです。</p> <p>```python:要素<br /> class Element:<br /> """<br /> Element(data, next)</p> <pre><code>LinkedListのそれぞれの要素のクラス。 dataはこの要素が表すデータ。 nextはこの次の要素の参照。 """ def __init__(self, data, next=None): self.data = data self.next = next </code></pre> <pre><code><br />次にリスト本体を実装してみます。 ```python:LinkedList class LinkedList: def __init__(self): self.first = None @property def is_empty(self): return self.first is None def append(self, data): if self.is_empty: self.first = Element(data) return nxt = self.first while nxt.next is not None: nxt = nxt.next nxt.next = Element(data) def pop(self): if self.is_empty: raise ValueError nxt = self.first while nxt.next is not None: nxt = nxt.next # 最後の要素のデータを一時変数に退避 last = nxt.next.data # 最後の要素への参照を消す nxt.next = None return last def remove(self, idx): # 最初ならself.firstを変更 if idx == 0: f = self.first self.first = f.next return f.data size = len(self) if idx >= size or idx < 0: raise IndexError(idx) if self.is_empty: raise ValueError # 最後ならpop if idx == size - 1: return self.pop() nxt = self.first for _ in range(idx - 1): nxt = nxt.next rem = nxt.next nxt.next = rem.next return rem.data def insert(self, idx, data): # 最初ならself.firstを変更 if idx == 0: self.first = Element(data, self.first) return size = len(self) if idx > size or idx < 0: raise IndexError(idx) # 最後+1ならappend if idx == size: self.append(data) return nxt = self.first for _ in range(idx): nxt = nxt.next nxt.next = Element(data, nxt.next) def __iter__(self): nxt = self.first while nxt.next is not None: yield nxt.data nxt = nxt.next def __len__(self): if self.is_empty: return 0 nxt = self.first ret = 1 while nxt.next is not None: nxt = nxt.next ret += 1 return ret def __getitem__(self, idx): if idx >= len(self) or idx < 0: raise IndexError(idx) if self.is_empty: raise ValueError nxt = self.first for _ in range(idx): nxt = nxt.next return nxt.data def __setitem__(self, idx, val): if idx >= len(self) or idx < 0: raise IndexError(idx) if self.is_empty: raise ValueError nxt = self.first for _ in range(idx): nxt = nxt.next nxt.data = val </code></pre> <p>こんな感じですね。<br /> これが一番単純なLinkedListの実装です。<br /> ですけど、結構非効率的ですね。</p> <p>例えば、一番後ろのデータを取得するために最初から全部辿っています。<br /> データにアクセスするためにすべて前からアクセスするのでは、後半のデータにアクセスする際に非効率になってしまいます。<br /> これを解決するのが双方向リストというものです。</p> <p>(次回があれば)これを双方向リストに改造してみたいと思います。<br /> 今日のところはここまでにしましょう。それがいい。</p> frodo821 tag:crieit.net,2005:PublicArticle/14801 2019-02-15T01:21:35+09:00 2019-02-25T15:21:06+09:00 https://crieit.net/posts/Python-HTML 純粋なPythonでHTMLを記述するライブラリを作ったから使ってほしいだけの記事 <h2 id="TL;DR"><a href="#TL%3BDR">TL;DR</a></h2> <p>Crieitに記事を投稿するのはすさまじく久しぶりな気がしますが…。<br /> おはようございます、こんにちは、今晩は。Frodoです。</p> <p>さて、今回は自作ライブラリをPyPIに登録したので使ってみてほしいというだけの記事になりますw<br /> どういうライブラリを作ったのかと言いますと、純粋なPythonの構文だけで比較的分かりやすくHTMLを構築するライブラリです。</p> <p>HTMLのテンプレートライブラリと言えばPythonではJinjaが有名でしょうか。<br /> ですが、JinjaはPythonの文法をほとんど全くフォローしておらず、新しくまったく別な言語を覚えることになってしまいます。<br /> よく使われているWebフレームワークのDjangoのテンプレート言語も似たようなものですね。</p> <p>そこで、純粋にPythonの言語機能のみを使ってHTMLを構築できないかと作ったのがRattle.pyです。<br /> Rattle.pyには次のような特徴があります。</p> <ol> <li>小型・軽量であること。Rattle.pyには依存しているライブラリがありません。</li> <li>純粋なPythonの文法のみでHTMLが構築できること。HTMLやPythonが分かっていれば覚えることはほとんどありません。</li> <li>日本語でサポートが受けられること。開発者が日本人ですから当然です。もちろん、英語でのサポートもあります。</li> </ol> <p>説明してもイメージがわかないと思いますので、実際に使ってみたいと思います。</p> <h2 id="実際に使ってみる"><a href="#%E5%AE%9F%E9%9A%9B%E3%81%AB%E4%BD%BF%E3%81%A3%E3%81%A6%E3%81%BF%E3%82%8B">実際に使ってみる</a></h2> <p>まずはインストールしましょう。話はそれからです。</p> <pre><code class="shell">$ pip install rattlepy </code></pre> <p>このように、普通のライブラリと同様pipで簡単に導入できます。<br /> 使うのも驚くくらい簡単です。</p> <pre><code class="python">from rattlepy.templating import ( html, body, head, title, h1, p, div, span, text, meta, link) with html(lang='ja') as elem: with head(): meta(charset='utf-8') meta(name='viewport', content='initial-scale=1.0;width=device-width') link(href='main.css', rel='stylesheet', type='text/css') with title(): text("Hello, Rattle.py!") with body(): with div(className='container'): with h1(): text("Hello, Rattle.py!") with p(): text("Rattle.py is a html templating library.") with span(className='emphasized'): text("This library can support you to make HTML in pure Python.") print(elem) </code></pre> <p>これを実行するとこうなります。</p> <pre><code class="html"><html lang="ja"> <head> <meta charset="utf-8"/> <meta name="viewport" content="initial-scale=1.0;width=device-width"/> <link href="main.css" rel="stylesheet" type="text/css"/> <title> Hello, Rattle.py! </title> </head> <body> <div class="container"> <h1> Hello, Rattle.py! </h1> <p> Rattle.py is a html templating library. <span class="emphasized"> This library can support you to make HTML in pure Python. </span> </p> </div> </body> </html> </code></pre> <p>このように、非常に簡単にHTMLが記述できるのです。<br /> また、これは純粋なPythonですから、何も考えずにPythonの変数を使ったり関数を呼び出したりできます。<br /> これRattle.pyの特徴です。</p> <h2 id="興味を持っていただけたら"><a href="#%E8%88%88%E5%91%B3%E3%82%92%E6%8C%81%E3%81%A3%E3%81%A6%E3%81%84%E3%81%9F%E3%81%A0%E3%81%91%E3%81%9F%E3%82%89">興味を持っていただけたら</a></h2> <p>このライブラリは開発中で、いろいろと未実装の機能が多くあります。<br /> また、使って頂き、フィードバックや意見がいただけると、それがどのようなものであっても励みになります。</p> <p><a target="_blank" rel="nofollow noopener" href="https://github.com/frodo821/rattlepy">GitHubのリポジトリ</a>もぜひチェックしてみてください。スターなど頂けたら激しく喜びます。<br /> フィードバックや意見は<a target="_blank" rel="nofollow noopener" href="https://twitter.com/BoufrawFrodo2">@BoufrawFrodo2</a>にメンションして送っていただくか、GitHubにissueを立てて頂ければチェックします。<br /> 完全に宣伝な記事でしたが、最後までお付き合いありがとうございました!</p> frodo821 tag:crieit.net,2005:PublicArticle/14705 2019-01-01T00:00:27+09:00 2019-01-01T10:42:41+09:00 https://crieit.net/posts/Python-5c2a2f0bb5a45 あけおめからPython <h1 id="皆さんへのメッセージです。"><a href="#%E7%9A%86%E3%81%95%E3%82%93%E3%81%B8%E3%81%AE%E3%83%A1%E3%83%83%E3%82%BB%E3%83%BC%E3%82%B8%E3%81%A7%E3%81%99%E3%80%82">皆さんへのメッセージです。</a></h1> <pre><code class="python">謹賀, 新年=(lambda x:(not x,x))([]) 新年.append((謹賀+謹賀)**((謹賀+謹賀)**(謹賀+謹賀+謹賀))-(謹賀+謹賀+謹賀)**(謹賀+謹賀+謹賀)-(謹賀+謹賀)) 新年.append((謹賀+謹賀)**((謹賀+謹賀)**(謹賀+謹賀+謹賀)-謹賀)+謹賀) 新年.append(新年[-謹賀]+謹賀) 新年.append(新年[謹賀-謹賀]) 新年.append(新年[謹賀]) 新年.append(新年[謹賀+謹賀]+((謹賀+謹賀)*(謹賀+謹賀))**(謹賀+謹賀)-謹賀) 新年.append(新年[謹賀-謹賀]) 新年.append(新年[謹賀]) 新年.append(新年[謹賀+謹賀]+(謹賀+謹賀)**(謹賀+謹賀+謹賀)) 新年.append(新年[謹賀-謹賀]) 新年.append(新年[謹賀+謹賀]) 新年.append(新年[謹賀]) print(bytes(新年).decode('utf-8')) </code></pre> <p>実行してみましょう。</p> <h1 id="小ネタは置いといて。"><a href="#%E5%B0%8F%E3%83%8D%E3%82%BF%E3%81%AF%E7%BD%AE%E3%81%84%E3%81%A8%E3%81%84%E3%81%A6%E3%80%82">小ネタは置いといて。</a></h1> <p>この前、ふとリスト内包表記の中身(ローカル変数空間)が気になったので以下のコードを実行してみました。</p> <pre><code class="python">[i for i in [0] if print(locals()) or 1] </code></pre> <p>結果はこうなりました。</p> <pre><code class="python">{'i': 0, '.0': <tuple_iterator object at 0x0000018E8BDDE518>} </code></pre> <p>はい、コードの実行フレームが分かれていますね。ところで表示された辞書オブジェクトのキー<code>'.0'</code>って何でしょう?気になったので以下のコードを実行してみました。もし、私の予想が正しければ実行は終了しないはずです。</p> <pre><code class="python">[i for i in [0] if locals().__setitem__('.0', range(2).__iter__()) or 1] </code></pre> <p>実際にやってみた結果、<code>[0]</code>が出力されました。どういうことなんでしょう…?<br /> <code>__setitem__</code>をした後に<code>print(locals())</code>で中身を調べてみます。</p> <pre><code class="python">[i for i in [0] if locals().__setitem__('.0', range(2).__iter__()) or print(locals()) or 1] </code></pre> <p>結果はこのようになりました。</p> <pre><code class="python">{'i': 0, '.0': <tuple_iterator object at 0x0000018E8BDDE518>} </code></pre> <p>要素を変えることができていません。なぜでしょう? 残念なことにいくつか仮説を立ててみたのですが、そのどれもが間違っていました。</p> <h2 id="仮説1. ローカル変数空間はループごとに新しく作られている"><a href="#%E4%BB%AE%E8%AA%AC%EF%BC%91.+%E3%83%AD%E3%83%BC%E3%82%AB%E3%83%AB%E5%A4%89%E6%95%B0%E7%A9%BA%E9%96%93%E3%81%AF%E3%83%AB%E3%83%BC%E3%83%97%E3%81%94%E3%81%A8%E3%81%AB%E6%96%B0%E3%81%97%E3%81%8F%E4%BD%9C%E3%82%89%E3%82%8C%E3%81%A6%E3%81%84%E3%82%8B">仮説1. ローカル変数空間はループごとに新しく作られている</a></h2> <p>これは簡単に確かめることができます。</p> <pre><code class="python">[i for i in [0,1] if print(id(locals())) or 1] </code></pre> <p>これで表示される数が2つとも等しければ使いまわされていることが証明できます。私が実行した結果はこうなりました。</p> <pre><code class="python">1711743637328 1711743637328 [0, 1] </code></pre> <p>はい、一致していますね。異なるオブジェクトのIDが一致することはまずありえませんので、ローカル変数空間は使いまわされていることがわかりました。</p> <h2 id="仮説 2. '.0'というキーは例外的に書き換えできない"><a href="#%E4%BB%AE%E8%AA%AC+2.+%27.0%27%E3%81%A8%E3%81%84%E3%81%86%E3%82%AD%E3%83%BC%E3%81%AF%E4%BE%8B%E5%A4%96%E7%9A%84%E3%81%AB%E6%9B%B8%E3%81%8D%E6%8F%9B%E3%81%88%E3%81%A7%E3%81%8D%E3%81%AA%E3%81%84">仮説 2. '.0'というキーは例外的に書き換えできない</a></h2> <p>当然なんてことはありませんでした。</p> <pre><code class="python">>>> x = {} >>> x['.0'] = 12 >>> x {'.0': 12} >>> x['.0'] = 4 >>> x {'.0': 4} </code></pre> <h2 id="仮説 3. ローカル名前空間のオブジェクトがdictではない"><a href="#%E4%BB%AE%E8%AA%AC+3.+%E3%83%AD%E3%83%BC%E3%82%AB%E3%83%AB%E5%90%8D%E5%89%8D%E7%A9%BA%E9%96%93%E3%81%AE%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%81%8Cdict%E3%81%A7%E3%81%AF%E3%81%AA%E3%81%84">仮説 3. ローカル名前空間のオブジェクトがdictではない</a></h2> <p>だんだん投げやりになってきましたね。当然そんなわけなく、<code>dict</code>でした…。</p> <pre><code class="python">>>> [i for i in [0] if print(type(locals())) or 1] <class 'dict'> [0] </code></pre> <p>とまぁ、こんな感じで<code>Python</code>サイドからの解明は終ぞできませんでした…。今年はPythonのコードリーディングをしようかと考えています。</p> <h1 id="リスト内包表記は単体でチューリング完全と聞いたので"><a href="#%E3%83%AA%E3%82%B9%E3%83%88%E5%86%85%E5%8C%85%E8%A1%A8%E8%A8%98%E3%81%AF%E5%8D%98%E4%BD%93%E3%81%A7%E3%83%81%E3%83%A5%E3%83%BC%E3%83%AA%E3%83%B3%E3%82%B0%E5%AE%8C%E5%85%A8%E3%81%A8%E8%81%9E%E3%81%84%E3%81%9F%E3%81%AE%E3%81%A7">リスト内包表記は単体でチューリング完全と聞いたので</a></h1> <p>リスト内包表記だけでいくつか書いてみました。まずは普通にFizzBuzzから。</p> <pre><code class="python"># FizzBuzz in list comprehension [('Fizz' if not i%3 else '')+('Buzz' if not i%5 else '')+(str(i) if i%3 and i%5 else '') for i in range(20)] </code></pre> <p>続いてはAtCoder Beginners Selectionより<a target="_blank" rel="nofollow noopener" href="https://atcoder.jp/contests/abs/tasks/abc088_b">Card Game for Two</a>を解くプログラム。結構強引ですね。</p> <pre><code class="python">print(sum(x-y for x, y in zip(*(lambda x:((x.append(0) if len(x)%2 else None),x.sort(reverse=True),(x[::2],x[1::2]))[2])((input(), [int(i) for i in input().split()])[1])))) </code></pre> <p>正直言うと、自分も難読化以上の意味はないと思いました…。</p> <p>ですが、いろいろやっているとやはり内包表記のポテンシャルは高いと感じました。あなたもやってみてはいかが?</p> <h1 id="for文で遊ぶ"><a href="#for%E6%96%87%E3%81%A7%E9%81%8A%E3%81%B6">for文で遊ぶ</a></h1> <pre><code class="python">for i in range(20): print(i) </code></pre> <p>と同じプログラムを<code>range</code>関数なしで黒魔術っぽくやってみたいと思います。</p> <pre><code class="python">for i in (lambda x: (x, x.append(x))[0])([]): print(len(i)-1) i.append(i) if i[20:]: i.clear() </code></pre> <p>循環参照をうまく使ってみました。難読化以上の意味はないです</p> <p>私からは以上です。皆さん、今年も元気に行きましょう!</p> frodo821