フォントについて

Google Fontsって久しぶりに覗いたのだけれど、すげー使いやすいっていうか、UIが洗練されてるっていうか。とりあえず行ってみていじってみると、使い方を調べるまでもなく、直感的に目的が果たせてしまうのが凄い。

一応手順を書いておくと

  1. 気に入ったフォントを選んで、そのボックスの右上にある+マークを押す。
  2. ウィンドウ右下にポップアップが出てくるので、そこをクリック
  3. CDNのURLが出てくるので、それをヘッダーにコピペ。
  4. 更にCSSの設定(↓こうゆうの)まで出してくれるので、それもコピペ。

font-family: 'Anton', sans-serif;

で完了。わぁ、超簡単。日本語のフォントは流石に少ないけれど(僕が見た時は全部で8個しかなかった)、それでも無料で利用出来て、しかもCDNでぶっこ抜き放題っていうのはありがたいよね。

あと一応、使うフォントがセリフ体なのかサンセリフ体なのかは把握しておいた方が良いと思う。更に言うと等幅フォントなのかどうかとか、クリア体なのかとか、まーその辺こだわっちゃうとキリがないのだけれど、大体の場合においてCSS上で 「指定フォントこれ、でもダメだったらサンセリフ体のなにか」みたいな書き方をすると思うので、一応そのあたりをサラッと勉強しておくのも良いかもしれない。何年か前にデザインについての講演を無茶振りされて、ほぼ「伝わるデザイン」というサイトの内容を丸パクリしてプレゼンした事があって、偶然見つけた割にはすごく良いサイトだったので、興味のある人は見てみるの超オススメ。

アンパッキングその1

アンパッキングその1

競技プログラミングで最初に躓くであろうデータストラクチャのアンパッキング。Pythonだと「そんなんでいいんかい!」っていうシンタックスがあるので、まずはそれらに慣れる事が重要だと思われる。

numbers = (1,2,3,4,5)
a,b,c,d,e = numbers

print(a)
print(b)
print(e)
print(d)
print(c)

# 1
# 2
# 5
# 4
# 3

こんなんで良い。

↑ではtupleを使ったけれど、リストでも良い。 尚、以降はドラクエ(細かく言うとドラクエ3)を例に取って説明していく。日本人的にイメージがしやすいと思ったからなのだけれど、ドラクエ3を知らない人には何も言うことはない。

infernos = ['ギラ', 4, ('HERO','MAGE','SAGE')]

name_japanese, mp_cons, jobs = gira

print(name_japanese)
print(mp_cons)
print(jobs)
ギラ
4
('HERO', 'MAGE', 'SAGE')

【解説】 giraという変数にリストをアサイン。そのリストの中には 1. その魔法の日本語名 2. 消費MP 3. その魔法を覚えることが出来る職業 が入っている。更に(3)の職業に関してはtupleで、複数の文字列が格納されている。

これも同じパターンで、順番にコレとコレとコレを格納ーって感じでアサインすればOK。順番と数だけ間違わなければ、問題なく各変数に割り当てられる。(ちなみに数を間違うとValueErrorが返ってくる。)

ここまでtupleとlistを相手にしてきたけれど、iterableなら実は何でもOK。文字列自体もiterableなので、同じような事が出来る。Pythonすごい。

informore = 'ベギラマ'

s_1, s_2, s_3, s_4 = informore

print(s_1)
print(s_2)
print(s_3)
print(s_4)
ベ
ギ
ラ
マ

あと、途中にある値を無視したい場合。例えばさっきの例で

infernos = ['ギラ', 4, ('HERO','MAGE','SAGE')]

真ん中にある消費mpは無視したい場合、適当な捨て変数に割り当てるっていう強引な技がある。だいたいアンダースコア_が使われる事が多いような気がする。

gira = ['ギラ', 4, ('HERO','MAGE','SAGE')]

name_japanese, _, jobs = gira

The Most Wanted Letter

chekioの問題やろうぜシリーズ


問題文

テキストが与えられるので、その中で一番頻出のアルファベットを返せっていうミッション。いくつか補足があって、

  • 大文字小文字は同じ扱い。Aとaは同じアルファベットとして扱う事。
  • カンマとかの記号も出てくるけど、それは無視する事。スペースも含む。
  • もし同じカウントのアルファベットが重複したら、abc順で一番最初に出てくるものを返す事。(つまりAが最優先でZがその逆)

最後の条件が一番厄介臭いけれど、とりあえずやってみよう!

#### 右に出てくるヒントに沿って解く方法

import string

def checkio(text: str) -> str:
    text = text.lower()
    c_list = {}
    for c in text:
        if c in string.ascii_lowercase:
            if c in c_list:
                c_list[c] += 1
            else:
                c_list[c] = 1
        
    list_of_item = list(c_list.items())
    list_of_item.sort(key=lambda t:(-t[1],t[0]))
    return list_of_item[0][0]

sortマスターになろうぜ!っていうテーマなのだと思うのだけれど、最後のlambdaを使ってのソートはなかなかレベルが高いような・・・。

でもディスカッションのスレッドで素晴らしいコメントを見つけたので紹介するよ。

sort()について。まず普通のリストに普通に掛けると、昇順で並び替えられる。当たり前だね。
l = [2,3,1]
sorted_l = l.sort()
sorted_l #[1,2,3]
で、sort()にはkeyっていう任意の引数があって、関数を指定できるようになっている。わかりすい所で言うとlenを取れば、単語の長い順にソートしてくれちゃったりする。
list_of_abc = ['aaaa', 'bbbbbbb', 'cc', 'd', 'eee']
list_of_abc.sort(key=len)

print(list_of_abc)

# ['d', 'cc', 'eee', 'aaaa', 'bbbbbbb']
で、ここにlambda関数を置いちゃえばいいんじゃね?っていうのが今回の問題のミソであり難所。普通はreverse引数を使うけれど、あえてlambdaで降順にソートしてみる。
list_of_numbers = [2, 1, 3]
list_of_numbers.sort(key=lambda x:-x)
print(list_of_numbers) #[3, 2, 1]
さらにこれを応用すると、「tupleの2番目の要素をまず昇順で並び替えて、そこから1番目の要素を降順で並び替えてね!」なんていう細かい指定も下記のように出来る。
list_of_tuples = [(1, 'a'), (1, 'b'), (2, 'a'), (2, 'b')]
list_of_tuples.sort(key=lambda t:(t[1], -t[0]))
print(list_of_tuples) # [(2, 'a'), (1, 'a'), (2, 'b'), (1, 'b')]

House Password

chekioの問題やろうぜシリーズ

問題文

斉藤くんはセキュリティに対する意識が低すぎて、いつも糞単純なパスワードを設定している。「ちゃんとしたパスワードかどうかをチェックするプログラム」を作るのが今回のミッションだ!今回定義する「ちゃんとしたパスワード」とは

  • 10文字以上で
  • 1文字以上の数字が含まれていて
  • 1文字以上の小文字と1文字以上の大文字が含まれている

この条件を満たすものとする。ちなみに使われる文字はASCII文字のみ。現実のWebサービスなどでもとてもよく見られるパスワードパターンだ。

おそらくchekioを初めて最初に取り組む事になる問題。問題自体よりもchekioのUIに戸惑ったりするかもしれないけれど、ちょっとやればすぐ慣れる!レッツゴー。

まずは超初心者向け回答から。

def checkio(data: str) -> bool:
    length_check = False
    if len(data) >= 10:
        length_check = True
    
    number_check = False
    for c in data:
        if c.isdigit():
            number_check = True
            break
    
    lower_check = False
    for c in data:
        if c.islower():
            lower_check = True
            break
    
    upper_check = False
    for c in data:
        if c.isupper():
            upper_check = True
            break
    
    if length_check == True and number_check == True and lower_check == True and upper_check == True:
        return True
    else:
        return False

条件チェッカーを作り、デフォルトをFalseに設定。チェッカーが通れば値がTrueに変わり、最後に全部のチェッカーがTrueになっていれば回答としてTrueを、そうでなければFalseを返すっていうやつ。ちょっとプログラミングをかじっている人が見たら「なんだこの冗長な糞コード」と思われるのも無理はないけれど、色々と学ぶべき事も多い。

isdigit(), islower(), isupper()など、教則本の最初の方に載っていて、結局いつ使うのか不明なメソッドを活用するチャンス!dataで与えられた文字を一個一個確認していくので、forループで回す。一応気持ちだけ処理速度を上げる為に、チェッカーがTrueになった段階でbreakを掛けている。

正規表現モジュールを使う場合

おそらくこれがマジョリティな方針になると思う。個人的には正規表現ってなんだか記号が多くて気持ち悪いので、好きではない。

import re

DIGIT_RE = re.compile('\d')
UPPER_CASE_RE = re.compile('[A-Z]')
LOWER_CASE_RE = re.compile('[a-z]')

def checkio(data):
    """
    Return True if password strong and False if not
    
    A password is strong if it contains at least 10 symbols,
    and one digit, one upper case and one lower case letter.
    """
    if len(data) < 10:
        return False
    
    if not DIGIT_RE.search(data):
        return False

    if not UPPER_CASE_RE.search(data):
        return False

    if not LOWER_CASE_RE.search(data):
        return False
        
    return True

elseって言わないで通すようにするのが少しだけPythonista。

setを使う方法

import string

upper = set(string.ascii_uppercase)
lower = set(string.ascii_lowercase)
digits = set(string.digits)


def checkio(data):
    letters = set(data)
    return bool(len(data) >= 10 and upper & letters and lower & letters and digits & letters)

他の人の回答を眺めてて、個人的に好きだったのがこのsetを使う方法。なるほどねー!
あとstringライブラリはこうゆう類の問題でよく使うので、シンタックスを覚えておこうとまでは言わないけれど、こうゆうのがあるっていう事だけ覚えておくと便利。

lower = ('abcdefghijklmnopqrstuvwxz')

なんてイチイチ書いてたら超絶面倒臭い訳で。。

リスト内包表記について

これを使えるかどうかでPython熟練度がなんとなくバレてしまうっていうリスト内包表記、英語で言うとList Comprehension。シンタックスとしては

 

[ expression for item in iterable ]  

という単純なものなんだけど、これがどんどん複雑になってくると僕は訳がわからなくなってくる。まずは単純な例から・・・

one_to_five = []

for num in range(1, 6):

    one_to_five.append(num)

 

print(one_to_five)

>>> [1, 2, 3, 4, 5]

↑をリスト内包表記で表現すると

one_to_five = [ num for num in range(1, 6) ]

print(one_to_five)

[1, 2, 3, 4, 5]

ここまではなんとなくわかる。次にシンタックスの最初にある"expression"を色々いじってみる。

new_list = [ num**2 for num in range(1, 6) ]

>> [1, 4, 9, 16]

なんかイメージとしては、最初にexpression以外の所を書いて、そんで最後にそのnumなり何なりに「何をしたいか」を打つのが言葉の順番的にはわかりやすい。。

ifコンディションを入れる

なんかexcelでこうゆう操作するなーって思い出したんだけど、シンタックスとしては以下

[ expresssion for item in iterable if condition ]

今はなき世界のナベアツのネタで、「3の倍数だけ馬鹿になります」っていうのがあるんだけど、とりあえず3の倍数だけ抽出してみる。

san_baka = [str(num)+'!!!' for num in range(1, 41) if num%3 == 0]

print(san_baka)

>>> ['3!!!', '6!!!', '9!!!', '12!!!', '15!!!', '18!!!', '21!!!', '24!!!', '27!!!', '30!!!', '33!!!', '36!!!', '39!!!']

まずはOK。でも本当は「3の倍数と、3のつく数字だけ馬鹿になります」っていうネタなので、更に修正。 python

san_baka = [str(num)+'!!!' for num in range(1, 41) if num%3 == 0 or '3' in str(num)]

print(san_baka)

>>>['3!!!', '6!!!', '9!!!', '12!!!', '13!!!', '15!!!', '18!!!', '21!!!', '23!!!', '24!!!', '27!!!', '30!!!', '31!!!', '32!!!', '33!!!', '34!!!', '35!!!', '36!!!', '37!!!', '38!!!', '39!!!']

OKOK。31くらいで爆笑が取れるはず。 でもまだ完璧じゃなくて、「3の倍数と3がつく数字」以外はシュールに読み上げなくてはいけない。つまり"!!!"を付けたくない訳なので、更に修正する。

--- ここで10分googleで調べまくった ---

どうやらelseを付ける場合は文法が若干変わるっぽい。

san_baka = [str(num)+'!!!' 
if num%3 == 0 or '3' in str(num) else num for num in range(1, 41)]

print(san_baka)

>>> [1, 2, '3!!!', 4, 5, '6!!!', 7, 8, '9!!!', 10, 11, '12!!!', '13!!!', 14, '15!!!', 16, 17, '18!!!', 19, 20, '21!!!', 22, '23!!!', '24!!!', 25, 26, '27!!!', 28, 29, '30!!!', '31!!!', '32!!!', '33!!!', '34!!!', '35!!!', '36!!!', '37!!!', '38!!!', '39!!!', 40]

参考にしたstack overflowのスレッド

stackoverflow.com

よしよし。これで最後の「40」を真顔に戻ってシュールに言う事で、第二の爆笑が取れるはずだ。ちなみにちょっと前に新宿のルミネ吉本行ったら落語家になったナベアツが出てたんだけど、個人的にはまったく面白くなかった。ごめんなさい。

リスト内包表記の中でfor文を回す

ネストされたループってあるじゃんか

rows = [1, 2, 3, 4]
cols = ['a', 'b', 'c', 'd']

for row in rows:
    for col in cols:
        print(row, col)

>>> 1 a
1 b
1 c
1 d
2 a
2 b
2 c
2 d
3 a
3 b
3 c
3 d
4 a
4 b
4 c
4 d

これを包括表記で打つと

cells = [(row, col) for row in rows for col in cols]
print(cells)

>>>[(1, 'a'), (1, 'b'), (1, 'c'), (1, 'd'), (2, 'a'), (2, 'b'), (2, 'c'), (2, 'd'), (3, 'a'), (3, 'b'), (3, 'c'), (3, 'd'), (4, 'a'), (4, 'b'), (4, 'c'), (4, 'd')]

こうなる。forが一行に2回出てくるとか、逆に読みにくいんじゃ・・・とも思う訳だけれど、きっと内部速度的には早くなっているのだろう。ただこれ、tupleの形になので、一応見た目だけそれっぽくするならexpressionの箇所を変えれば良い。

cells = ["{} {}".format(row, col) for row in rows for col in cols]
print(cells)

>>>  ['1 a', '1 b', '1 c', '1 d', '2 a', '2 b', '2 c', '2 d', '3 a', '3 b', '3 c', '3 d', '4 a', '4 b', '4 c', '4 d']

 じゃーここまでの知識で何かやってみよう

友愛数を見つけてそれをリスト化する」っていうのを内包表記内でやろうと思う。これ言ってる時点で思っているのが、「糞長い一行になりそう・・・」って事。あとさすがのmacbook先生でも、計算に時間がかかりそう・・・って事。

友愛数とは! 異なる2つの自然数の組で、自身を除いた約数の和が、互いに等しくなる数。ちなみに最小の友愛数は220と284。

Gitとその他VCSの違いについて

なんとなく会社でGit使っているからとか、チュートリアルの中でGitを使うように書いてあるからとかでGitを使い始める人も多いですね。僕もそうだったんだけど、最初に「なぜGitを使うのか」とか「Gitと他のVCSCVSとの違い)」とかを知っておくのは非常に重要だと、今になって思うのです。手段が目的化してしまいますからね。

あとなんとなく「基本的には他のCVSと同じだろう」って思うのは大きな罠です。UIが似てたりするのでよくそうゆう勘違いが起きますが、データの扱い方が全然違います。後から「あれ、なんでこうなってんだ?」っていう混乱を避けるために、Gitを学ぶ際はそれらの違いを最初に頭の中で明確にしておく必要があるのです。

一番大きな違いは、まず他のVCSに於いては常に前のバージョンのファイルとの差異にフォーカスを当てているのに対して、Gitはスナップショット(←これキーワード)を撮っているという事です。

Git以外の方式--例えばCVSだったりSubversionだったりPerforceだったりBazzarだったり--は一般的にdelta-based version controlと呼ばれていて、ファイルの更新をリスト化して保存しているイメージです。

それに対してGitは、commitコマンドを打つたびに、現状のファイル状態をまるっとスナップショットします。厳密に言うと差異をチェックはしているので、何も変更がない場合はそのスナップショットが自動でスキップされたりするのですが、まあそれはレアケースですね。

とても抽象的な話なので、ここまで自分で書いておいて「なんのこっちゃ」とも思う訳ですが、これは非常に重要な事なのです。後述するブランチングに深く関わってくる事なのですが、とりあえず「gitはスナップショット、他のVCSは差異保管」という事で、それは大きな違いだって事を頭に入れておけばOKです。

 ほぼすべての操作がローカルで行える

Gitの操作のほとんどはローカルで行えます。何かしようと思った時、サーバーなり他のクライアントなりを見に行く必要がありません。これが例えばCVCSを使っていたら、常に会社のサーバーと接続していないと作業が出来ないし、更にサーバーから常にレスポンスを待たなくてはいけないので、レイテンシが気になる時もあります。このあたりもGitの大きな強みで、スピードが重要視される現代にぴったりとマッチしているのですねー。

更新履歴を確認しようと思った時、Gitなら普通にすべての記録がローカルに書き込まれているので、それを見るだけです。一瞬でアクセス出来ます。前のバージョンの差異を確認したい時も同様で、ローカルですべて完結します。かつてのサーバー管理者に「すいません。。。前のバージョンのログ見たいのでプルリク送ってもいいですか?」と頭を下げる必要がないのです(かつては本当に行われていた)。

つまりGitで管理しているプロジェクトならば、ほぼすべての作業がオフラインで行えるという事です。飛行機の中でも作業可能っていうのは大きいんですよね。上空10000mでコミットとか格好いいですね。あと個人的にはあまり好きではないVPN、これ使っている会社にいる時でも、作業自体はローカルで完結します。いやー便利。