私が歌川です

@utgwkk が書いている

おすすめのキーボードを教えてください

急に他の人のキーボードの環境に興味が湧いてきた。現在は↑のマイクロソフトの1000円ぐらいのキーボードを使っているという状態です。

今のところそんなに不満はなくて、強いて言えばFnキーやらが小さくてストロークが浅いということぐらい。普段のタイピングでは不自由していないつもりだ。

似たようなモデルで(似ていなくても)もっといいキーボードがあるなどの情報をお待ちしております。

SKK-JISYO.touhou.utf8 を生成(変換)するスクリプトを書いた

SKK 使ってて東方Projectの用語を変換したくなることは多々あって,しかしながら辞書がないので地道に作っていくみたいな作業を強いられるのもつらいものです. したがって既存の辞書に乗っかる方向性が一番精神衛生によいです.

東方Project辞書 | Cue:LAB から Google 日本語入力向けの 東方Project辞書を落としてきて,下のスクリプトも落として,

gist.github.com

$ python thdic2skk.py < thdic-r6-0-総合.txt > SKK-JISYO.touhou.utf8

みたいにして辞書ファイルを投げてやると SKK 用の辞書が生成されます. スクリプトPython 2.7.12 と 3.5.2 で,変換は CorvusSKK ver2.4.5 で動作確認済です.

スローライフ・ファンタジー

www.youtube.com

スローライフ・ファンタジーになりたい.

スローライフを送ったことがないと思っていて,たぶん今が一番スローライフに近いと思うけど,それでも全然スローライフじゃないと思っている. 今までがずいぶん忙しい生活を送っていたと思う.

ホントに大事なものは そんなに多くないから
両手で持てる分だけ 大事にしよう

双葉杏はしきりに「楽するための努力は惜しまない」と言っている. これは実際私も共感するところであって,それでいて実現するのは難しい. いつも楽するための努力をしているつもりなのだが,なかなか楽をすることができずに,だいたい最後には苦労をすることになっている. 私と双葉杏との違いがそこにあるのではないだろうか? 才能であると言われればそこまでだろう.

たまに
美味しいものも食べたい
全身で喜びたい
クツを放り投げ 寝転びたい
空はキレイだ

クツを放り投げて寝転ぶような体験を最近していないことを思い出した. どんなに忙しくても,空はそこにあって,青かったり白かったり黒かったりする. 私たちが忘れていたのは,空の大きさと,それに気づくことができる心の余裕だったのではないだろうか?


2日連続で大きいサイズの麺類を食べていて、いまも胃腸がフル稼働しているような気がする

献血した

献血した.これで6回目.看護師さん(?)に「もう5回も献血してはりますね」と言われた.

昼休みに行ったところ*1,40分ぐらいで全部終わったのでお得.寒いせいか人が少なかった.

今回謝礼としてボールペンとポカリスエットの容器をもらった.

大塚製薬 ポカリスエット スクイズボトル

大塚製薬 ポカリスエット スクイズボトル

これです.でかくて持ち運びに苦労した.電解液を運ばなければならなくなったら使おうと思う.

追記 (2017/01/31)

血液検査の結果が出た. コレステロールの値が上昇傾向にあるので,次回には200を超えているであろうという予想をしている.

当たってたらこれください.

前回の献血

utgwkk.hateblo.jp

*1:大学には月1ぐらいで献血車が来る.

Nintendo Switch 予約成功した

gyazo.com

一緒にぷよぷよテトリスをやりましょう.

追記

ついでなので日記らしいことを書きます.

ゲーム機本体を発売日前に予約して購入した体験はたぶんこれが初めてだと思う. 初めて触ったゲーム機はゲームボーイで,マリオランドやポケモンをやっていたことだけ覚えている. うちはあまりゲームを買ってもらえない家庭で,だいたいはブックオフの中古ゲームを買っていくとか,いとこから譲ってもらうみたいな生活をしていた. まあ自分で稼いだお金じゃないんだからつべこべ言うなと言われるとそんなに反論はできない. 今回はちゃんとアルバイトで稼いだお金なのでよさそう. ぷよぷよテトリスしたい.

深夜テンションで書いたコードを長期間運用していくと発生すること

たとえばこのようなコードがある.Twitter でいいねした画像付きツイートを保存して,その URL を Slack に投稿して,サムネイル画像を生成して,必要な情報を DB に書き込むという処理をやっている.

client.on_event(:favorite) do |event|
  source = event[:source]
  target_object = event[:target_object]
  if source[:screen_name] == YOUR_SCREEN_NAME && !target_object[:user][:protected] && target_object[:extended_entities]
    id_str = target_object[:id_str]
    screen_name = target_object[:user][:screen_name]
    url = create_twitter_url(id_str, screen_name)
    download_urls = []
    target_object[:extended_entities][:media].size.times do |i|
      media = target_object[:extended_entities][:media][i]
      download_url = media[:media_url_https]
      download_urls << download_url
    end
    text = url
    puts text
    options[:text] = text
    Slack.chat_postMessage(options) if filtering(target_object)
    download_urls.each do |download_url|
      download_filepath = File.join(IMAGE_DOWNLOAD_DIR, File::basename(download_url))
      thumbnail_filepath = File.join(THUMBNAIL_DIR, File::basename(download_url))
      begin
        body = open(download_url + ':orig', &:read)
        download_url = download_url + ':orig'
      rescue OpenURI::HTTPError
        begin
          body = open(download_url, &:read)
        rescue OpenURI::HTTPError
          raise '画像のダウンロードに失敗しました'
        end
      end
      puts download_url
      File.binwrite(download_filepath, body)
      Magick::Image.from_blob(body).shift.resize_to_fit(128).write thumbnail_filepath
      db.transaction
      begin
        db.execute INSERT_SQL, download_filepath, screen_name, id_str, Time.now.to_f, target_object[:text]
        db.commit
      rescue
        db.rollback
      end
    end
  end
end

私はこれを見て,困惑しているところである.いろいろ思うところはあるが,とりあえず抜粋すると,

  • コメントがない
  • 行が空いていない
  • 処理ごとに変数が依存しあっている

コメントがない

悪い癖だと思う.自分だけのコードであっても,少し時間が経つと意図が不明になるというのはよくあることで,この条件式はこういう意味とかをとりあえず書いておけば,あとでそこを変更することになっても,現状に合うようにするだけで,タイムリープして条件式を解釈しなおす必要がなくなると思う.

行が空いていない

これもあまり良くないと思っている.先のコードは,もう少し噛み砕くと,次のような処理をやっている.

  1. いいねイベントを受け取る
  2. 自分のいいねイベントであり,いいねしたツイートにメディアが付いていて,かつツイートした人が限定公開アカウントでないなら次へ
  3. Slack に投稿する用にツイートの URL を生成する
  4. ダウンロードする画像の URL のリスト取得する
  5. URL を出力し,同時に Slack にも投稿する
  6. ダウンロードする画像の URL に対して,次のことを行う
    1. ダウンロード先のパスと,サムネイルを保存するパスを生成する
    2. 画像をダウンロードする
    3. サムネイルを生成して保存する
    4. DB に書き込む

これらの処理がぜんぶ,行を空けずに書いてあるので,初めて見た人にはどこまでがどの処理かが分かりづらくなっている.

処理ごとに変数が依存しあっている

私は,たとえば,ダウンロードとサムネイルの生成は別個で行われるべきだと考える. そうして download というメソッドと generate_thumbnail というメソッドに分けようと思うが,このコードを素直に切り貼りしただけではうまく動かない. サムネイルの生成が,ダウンロードに用いる body という変数(ダウンロードしたファイルのデータを表す)に依存しているのである!

この場合は,サムネイルの生成はダウンロードより後に行われるとしているので,切り分ける際には次のようになるだろうか.

def download(url, save_path)
  begin
    # :orig 付きでダウンロードしてみる
    body = open(url + ':orig', &:read)
  rescue OpenURI::HTTPError
    begin
      # だめなら :orig なし
      body = open(url, &:read)
    rescue OpenURI::HTTPError
      raise '画像のダウンロードに失敗しました'
    end
  end
end

def generate_thumbnail(from_path, to_path)
  File.open(from_path, 'rb') do |f|
    Magick::Image.from_blob(f.read).shift.resize_to_fit(128).write to_path
  end
end

basename = File::basename url

download_path = File::join DOWNLOAD_DIR, basename
download url, download_path

thumbnail_path = File::join THUMBNAIL_DIR, basename
generate_thumbnail download_path, thumbnail_path

まとめ

勢いよく書いて運用しているコードにあとから機能が必要になったと,これまた深夜テンションで継ぎ足していくと,あとで見直したときに行方不明になってしまう.

普段から疎結合なコードを書く癖がないとすぐこうなる,という典型例っぽい気がしてきた.みなさんは疎結合なコードを書くようにしましょう.あとテストも書きましょう.

常に正常な判断がしたい,正常とは……?

あわせて読みたい

utgwkk.hateblo.jp

utgwkk.hateblo.jp

utgwkk.hateblo.jp