Hatena::ブログ(Diary)

エンジニアのソフトウェア的愛情 このページをアンテナに追加 RSSフィード Twitter

2016-02-07

配列の回転

時計回りに90度 行を反転して転置(あるいは、転置して列を反転)
時計回りに180度 行を反転して列を反転(あるいは、列を反転して行を反転)
時計回りに270度 転置して行を反転(あるいは、列を反転して転置)
[1] pry(main)> a = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
=> [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
[2] pry(main)> a.reverse.transpose
=> [[7, 4, 1], [8, 5, 2], [9, 6, 3]]
[3] pry(main)> a.reverse.map(&:reverse)
=> [[9, 8, 7], [6, 5, 4], [3, 2, 1]]
[4] pry(main)> a.transpose.reverse
=> [[3, 6, 9], [2, 5, 8], [1, 4, 7]]

2016-01-29

逆波蘭記法計算機

備忘録


Haskell

operate :: [Integer] -> String -> [Integer]
operate (r:l:st) "+" = (l + r):st
operate (r:l:st) "-" = (l - r):st
operate (r:l:st) "*" = (l * r):st
operate (r:l:st) "/" = (l `div` r):st
operate stack    t   = (read t::Integer):stack

rpn :: String -> Integer
rpn s = case foldl operate [] (words s) of [answer] -> answer

$ ghci
Prelude> :l rpn.hs
*Main> rpn "12 32 + 4 / 1 - 10 *"
100

Ruby

def rpn(s)
  stack = []
  s.split.each do |token|
    if '+-*/'.include? token
      l, r = stack.pop(2)
      stack.push token.to_sym.to_proc[l, r]
    else
      stack.push token.to_i
    end
  end
  stack.last # 脇が甘い
end

$ irb
irb(main):001:0> require './rpn.rb'
=> true
irb(main):002:0> rpn '12 32 + 4 / 1 - 10 *'
=> 100

Prolog

words([], []) :- !.
words(S, [L|RS]) :- append(L, [0' |R], S), words(R, RS), !.
words(S, [S]) :- !.

rpn([], [Answer], Answer).
rpn(["+"|Tokens], [R,L|Stack], Answer) :- N is L + R, rpn(Tokens, [N|Stack], Answer).
rpn(["-"|Tokens], [R,L|Stack], Answer) :- N is L - R, rpn(Tokens, [N|Stack], Answer).
rpn(["*"|Tokens], [R,L|Stack], Answer) :- N is L * R, rpn(Tokens, [N|Stack], Answer).
rpn(["/"|Tokens], [R,L|Stack], Answer) :- N is L / R, rpn(Tokens, [N|Stack], Answer).
rpn([T|Tokens], Stack, Answer) :- number_codes(N, T), rpn(Tokens, [N|Stack], Answer).

rpn(S, Anser) :- words(S, Tokens), rpn(Tokens, [], Anser), !.

$ swipl --traditional

?- ['rpn.prolog'].
true.

?- rpn("12 32 + 4 / 1 - 10 *", N).
N = 100.

2016-01-10

iTunes の CD の情報を JavaScript で設定する

CD を購入した時に、iTunes 上で情報を設定するのが面倒だったのでスクリプトでなんとかしようと思ったらなんとかなったので、そのメモ。

従来から AppleScript で iTunes を操作することはできましたが、AppleScript は不得意なため Yosemite から使えるようになった JavaScript を利用しています。

加えて、CoffeeScriptコンパイル結果をコンソールに出力して、パイプで流し込んでやれば CoffeeScript でもやりたいことができるので、最終的には CoffeeScript で書いています。



情報の取得方法と設定方法を確かめる

まずは osascript の interaction mode で一つずつ動作を確認してます。

osascript を interactive mode で言語に JavaScript を指定して起動します。

$ osascript -l JavaScript  -i
>>

iTunes のオブジェクトを取得します。

>> itunes = Application('iTunes')
=> Application("iTunes")

iTunes アプリケーションは sources という要素を持っています。

どのような要素を持っているかはスクリプトエディタ用語説明で閲覧することができます。用語説明の開き方は後ろの方で書きます。

>> itunes.sources()
=> [Application("iTunes").sources.byId(64), Application("iTunes").sources.byId(25404), Application("iTunes").sources.byId(145406)]

sources は クラス Source の集まりで、Source には kind というプロパティがあります。kind の値を表示させてみると audio CD となっているものがあります。この source が CD です。

>> for(var source of itunes.sources()) { console.log(source.kind()); }
library
radio tuner
audio CD
=> undefined
>> itunes.sources[2].kind()
=> "audio CD"

同様に Source は クラス Playlist の集まりである playlists という要素を持っています。通常は数は1つのようです。

>> itunes.sources[2].playlists()
=> [Application("iTunes").sources.byId(145804).audioCDPlaylists.byId(145805)]
>> itunes.sources[2].playlists[0]()
=> Application("iTunes").sources.byId(145804).audioCDPlaylists.byId(145805)

同様に Playlist は クラス Track の集まりである tracks という要素を持っています。tracks の各要素が CD のトラック情報になります。

>> itunes.sources[2].playlists[0].tracks()
=> [Application("iTunes").sources.byId(145804).audioCDPlaylists.byId(145805).audioCDTracks.byId(145812),  (...以下、トラックの数だけオブジェクトが並ぶ...)]

Track には name というプロパティがあります。これがトラックの名前です。

>> for(var track of itunes.sources[2].playlists[0].tracks()) { console.log(track.name()); }
(...CD の全トラックの名前が並ぶ...)

プロパティに値を代入することで設定をすることができます。

この辺り、一般的な JavaScript と記述の方法が違いますので注意してください(実態は JavaScript にマップされた AppleScript のらしい)。

itunes.sources[2].playlists[0].tracks[0].name = 'This Will Be the Day'


CoffeeScript で情報を一覧する

CD のトラック情報を一覧する CoffeeScript です。

itunes = Application('iTunes')

for source, source_index in itunes.sources()
  continue if source.kind() != 'audio CD'
  for playlist, palylistIndex in source.playlists()
    console.log """
      PLAYLIST #{palylistIndex}

      duration:    #{playlist.duration()} sec
      name:        #{playlist.name()}
      loved:       #{playlist.loved()}
      shuffle:     #{playlist.shuffle()}
      size:        #{playlist.size()} byts
      songRepeat:  #{playlist.songRepeat()}
      specialKind: #{playlist.specialKind()}
      time:        #{playlist.time()}
      visible:     #{playlist.visible()}
    """

    for track, trackIndex in playlist.tracks()
      console.log """

        TRACK #{trackIndex}

        album:            #{track.album()}
        albumArtist:      #{track.albumArtist()}
        albumLoved:       #{track.albumLoved()}
        albumRating:      #{track.albumRating()}
        albumRatingKind:  #{track.albumRatingKind()}
        artist:           #{track.artist()}
        bitRate:          #{track.bitRate()}
        bookmark:         #{track.bookmark()}
        bookmarkable:     #{track.bookmarkable()}
        bpm:              #{track.bpm()}
        category:         #{track.category()}
        comment:          #{track.comment()}
        compilation:      #{track.compilation()}
        composer:         #{track.composer()}
        databaseID:       #{track.databaseID()}
        dateAdded:        #{track.dateAdded()}
        description:      #{track.description()}
        discCount:        #{track.discCount()}
        discNumber:       #{track.discNumber()}
        duration:         #{track.duration()}
        enabled:          #{track.enabled()}
        episodeID:        #{track.episodeID()}
        episodeNumber:    #{track.episodeNumber()}
        eq:               #{track.eq()}
        finish:           #{track.finish()}
        gapless:          #{track.gapless()}
        genre:            #{track.genre()}
        grouping:         #{track.grouping()}
        itunesu:          #{track.itunesu()}
        kind:             #{track.kind()}
        longDescription:  #{track.longDescription()}
        loved:            #{track.loved()}
        lyrics:           #{track.lyrics()}
        modificationDate: #{track.modificationDate()}
        playedCount:      #{track.playedCount()}
        playedDate:       #{track.playedDate()}
        podcast:          #{track.podcast()}
        rating:           #{track.rating()}
        ratingKind:       #{track.ratingKind()}
        releaseDate:      #{track.releaseDate()}
        sampleRate:       #{track.sampleRate()}
        seasonNumber:     #{track.seasonNumber()}
        shufflable:       #{track.shufflable()}
        skippedCount:     #{track.skippedCount()}
        skippedDate:      #{track.skippedDate()}
        show:             #{track.show()}
        sortAlbum:        #{track.sortAlbum()}
        sortArtist:       #{track.sortArtist()}
        sortAlbumArtist:  #{track.sortAlbumArtist()}
        sortName:         #{track.sortName()}
        sortComposer:     #{track.sortComposer()}
        sortShow:         #{track.sortShow()}
        size:             #{track.size()}
        start:            #{track.start()}
        time:             #{track.time()}
        trackCount:       #{track.trackCount()}
        trackNumber:      #{track.trackNumber()}
        unplayed:         #{track.unplayed()}
        videoKind:        #{track.videoKind()}
        volumeAdjustment: #{track.volumeAdjustment()}
        year:             #{track.year()}
      """

これを例えば cd-info.coffee というファイル名に保存し、coffee コマンドでコンパイルした結果を osascript にパイプで流し込むとスクリプトが実行されます。

$ coffee -p -c cd-info.coffee | osascript -l JavaScript


用語説明を開く

まずスクリプトエディタを起動します。

スクリプトエディタのファイルメニューから用語説明を開くを選択します。

f:id:E_Mattsan:20160110201704p:image


開いたダイアログから iTunes を選択して選択ボタンを押します。

f:id:E_Mattsan:20160110201703p:image


擁護説明のウィンドウが開きます。ただし開いた直後の状態では AppleScript での説明になっているので言語を変更します。

f:id:E_Mattsan:20160110201701p:image


ウィンドウの上部にあるリストで言語を AppleScript から JavaScript に変更します。

f:id:E_Mattsan:20160110201700p:image


ウィンドウの表示が JavaScript のものになります。

f:id:E_Mattsan:20160110201656p:image



いつか読むはずっと読まない:The Custard Protocol

前作のソフロニアの時代から大きく下ってアレクシアの娘、プルーデンスの物語へ。

…ってソフロニアの物語の訳本の最終巻がまだ出てないんですけれど。

2015-12-30

第2回 ESM オフラインリアルタイムどう書くを開催しました

前回開催から2ヶ月あまり。12月24日に第2回を開催しました。

今回はわたしが問題を作成し、@mtsmfm に相談、調整をお願いしての出題になりました。


※公開後に寄せていただいたコードを追記しました。



問題を作る

開催が12月24日の午後からということから、今回の問題は星型ネタで行こうとまず決めました。

図形ネタということで最初に考えたのがグラフの経路もの。こうして考えたのが問題「星めぐり」でした。気がかりはいまひとつ面白みに欠ける気がするところ。


もうひとつ。前回の開催ではテストケースを書いて時間切れというパタンがいくつかありました。どのぐらいのペースでどのぐらいの量のコードを書けばよいか、参加者にその感覚を伝えられていなかったのがその原因。

その反省を踏まえて。先に雰囲気を感じてもらおうと例題も作成。考えたのが問題「瞬き星」。


そしていざ例題を公開しようと問題文を書いているうちに、実は例題として考えた問題の方が面白いんじゃね? と思い至り、急遽例題と当日の問題を入れ替えての公開となりました。



開催2週間前に例題を公開したところ、面白みに欠けるかもというわたしの不安をよそに、Common Lisp で書く、Neography (Ruby+Neo4j)で書く、C言語でループも分岐も使わずに書く、等々ひねったコードがよせられ、例題も一つの問題として楽しんでもらえたようでした。



開催当日

当日参加されたのは8人+オンラインで1人。

当日の他の予定との兼ね合いでコードを書く時間を40分と設定していたのですが、やはり40分という時間はコードを書くには半端な時間のようで休憩時間(兼バッファ)として用意していた時間も当てて計1時間で書いてもらいました。

自分が作成した問題を解いてもらうというのは、想像以上に緊張を覚えるもので、コードを書いている時間はずっとそわそわ。自分も先に書いたものとは別の言語で書いてみようと考えていたのですが、そんな余裕ありませんでした。

自覚はしてるんですが、ほんとメンタル弱いな、自分。


結果、そして痛恨の出題不備

1時間が経過。休憩を挟んで各々書いたコードの解説をお願いしました。

結局テストパタンがすべて通ったのはひとり。ほぼ解けていた人があと数人。結果としての1時間だったのですが、ほどほどの問題が用意できたようで、その点は少しほっとしました。

しかしここで出題不備が発覚。

出題の意図とは違う解釈で書かれた方いて、しかもその解釈であれば確かに期待する結果が得られるコード。なのに用意したテストの中に通らないものがある。

問題を読み返すと、確かにその解釈ととらえることもできます。

問題文において、曖昧さを排除することの難しさを改めて思い知らされました。



第2回を終えて

最後に参加者に一言ずつもらいました。

まず楽しかったという感想をもらえたのが何より嬉しく思いました。

そして今後のことはまだ何も話していないのに「次回は〜」という言葉が幾人もの口から出たこと。ハッカソンでやろうか、とか、時間をかけて解きたいから事前に書いて持ち寄って発表とかどうか、とか、アイディアも次々と出てきます。

今後はもっと「どう書く」にフォーカスして、オフラインリアルタイムだけでなく他の形式もいろいろ試みていけたらよいなと思いました。


最後にわたしの感想。「ほんと、みんなプログラミング好きだなw」



みんなの実装

所属する事業部の公用語Rubyのため、Ruby多めになっております。

もし、この記事や問題を見て解いてみた! という方がいらっしゃいましたら、こちらに追記させていただこうと思います。


当日

@fossamagna

@hidenba

@kajisha

@ikkun

@samurai3

@kunitoo


後日

@kunitoo

@tkm-kj



追記

問題公開後にコードを寄せていただきました。ありがとうございます!

こちらに追記させていただきました。


星めぐり

[@Nabetani / 鍋谷 さん

@cielavenir / しえる さん


瞬き星

@Nabetani / 鍋谷 さん

@cielavenir / しえる さん

@angel_p_57 / angel-p57 さん



いつか読むはずっと読まない:救済の技法

「どう書く」とは方向性は違いますが、プログラミングスキルを駆使するという共通点で紹介。



あと。過去にもこのブログで取り上げていますが、こちらの本も再掲しておきます。現在は入手困難な様子なのが悲しい。



バランスをとるためにもう一冊。コードが Fortran だったり、 PL/I だったり、現在のモダンプログラミングにはそぐわない部分があったりしますが、その考え方は今でも通用すると思います(今ならもっと適切な書籍がありそうですが…)。

特に規則の第一、「わかりやすく書こう。うますぎるプログラムはいけない。」は肝に銘じるべき規則と今でも信じています。

プログラム書法 第2版

プログラム書法 第2版



星めぐりの歌

宮澤賢治 星めぐりの歌


あかいめだまの さそり

ひろげた鷲の  つばさ

あをいめだまの 小いぬ、

ひかりのへびの とぐろ。


オリオンは高く うたひ

つゆとしもとを おとす、

アンドロメダの くもは

さかなのくちの かたち。


大ぐまのあしを きたに

五つのばした  ところ。

小熊のひたいの うへは

そらのめぐりの めあて。

2015-11-29

昔のお題をCypherで解いてみる

グラフデータベースならグラフ理論の問題ぐらい解けるだろう、という乱暴な短絡思考から昔のグラフのお題を解いてみました。

昔のお題がネタなら、今回の解法もネタだったりします。


とにかくCypher で書く

Cypher歴2日なので不手際があるかもしれません。

// see http://d.hatena.ne.jp/E_Mattsan/20120511/1336748744

// 番号付きの点を作る
UNWIND
  range(1, 20) AS number
CREATE
  (n:Point {number: number});

// 点から右方向と下方向の辺を作る
UNWIND
  // ただし右端と下端を除く
  [i IN range(1, 20) WHERE NOT (i % 4 = 0 OR i IN range(17, 20))] AS number
MATCH
  (n:Point {number: number}),
  (right:Point {number: number + 1}),
  (bottom:Point {number: number + 4})
CREATE
  (n)-[:Edge]->(right),
  (n)-[:Edge]->(bottom);

// 右端の点から下方向の辺を作る
UNWIND
  [i IN range(1, 4) | i * 4] AS number
MATCH
  (n:Point {number: number}),
  (bottom:Point {number: number + 4})
CREATE
  (n)-[:Edge]->(bottom);

// 下端の点から右方向の辺を作る
UNWIND
  range(17, 19) AS number
MATCH
  (n:Point {number: number}),
  (right:Point {number: number + 1})
CREATE
  (n)-[:Edge]->(right);

// 点1から点20への経路の個数を数える
MATCH
  (:Point {number: 1})-[*]->(:Point {number: 20})
RETURN
  count(*) AS count;

// 点6-点10, 点11-点12, 点14-点15 の辺を削除する
MATCH
  (_6:Point {number: 6}),
  (_10:Point {number: 10}),
  (_11:Point {number: 11}),
  (_12:Point {number: 12}),
  (_14:Point {number: 14}),
  (_15:Point {number: 15}),
  (_6)-[_6_10]->(_10),
  (_11)-[_11_12]->(_12),
  (_14)-[_14_15]->(_15)
DELETE
  _6_10,
  _11_12,
  _14_15;

// 点1から点20への経路の個数を数える
MATCH
  (:Point {number: 1})-[*]->(:Point {number: 20})
RETURN
  count(*) AS count;

実行してみる

上記のクエリを count_paths.cypher という名前で保存します。


neo4j-shell コマンドを使ってクエリを実行します。

Neo4j干渉するデータが入っていない状態で実行してください。

$ neo4j-shell -file count_paths.cypher 
+-------------------+
| No data returned. |
+-------------------+
Nodes created: 20
Properties set: 20
Labels added: 20
5 ms
+-------------------+
| No data returned. |
+-------------------+
Relationships created: 24
15 ms
+-------------------+
| No data returned. |
+-------------------+
Relationships created: 4
6 ms
+-------------------+
| No data returned. |
+-------------------+
Relationships created: 3
6 ms
+-------+
| count |
+-------+
| 35    |
+-------+
1 row
14 ms
+-------------------+
| No data returned. |
+-------------------+
Relationships deleted: 3
6 ms
+-------+
| count |
+-------+
| 15    |
+-------+
1 row
9 ms

それぞれ経路数が35, 15と出力されました。


いつか読むはずっと読まない:数学ガール新刊

結城先生のメッセージカードが入っていました。

f:id:E_Mattsan:20151129195419j:image