エンジニアのソフトウェア的愛情

または私は如何にして心配するのを止めてプログラムを・愛する・ようになったか

備忘録:aws-sdk gem を使って AWS S3 にファイルをアップロードする

require 'aws-sdk'

client = Aws::S3::Client.new(
          access_key_id: 'your-aws-access-key-id',
          secret_access_key: 'your-aws-secret-access-key',
          region: 'your-region')

client.put_object(
  bucket: 'target-backet-name',
  key: 'sample.txt',
  body: File.read('path/to/local/sample.txt'))

Aws::S3::Client#put_object - AWS SDK for Ruby V2

Elixir の OptionParser.parse はハイフンをアンダスコアに置き換える

OptionParser.parse(["--show-all"], [switches: [show_all: :boolean]])
# => {[show_all: true], [], []}
OptionParser.parse(["--show_all"], [switches: [show_all: :boolean]])
# => {[], [], [{"--show_all", nil}]}

Elixir converts switches to underscored atoms, so --source-path becomes :source_path.

OptionParser.parse - Elixir

「どう書く」をElixirで書いたときのテストを簡単にする

Elixir でコールバックってどうやって実装しているのだろうと調べてみたら、マクロで実現されていました。なるほど。 その昔 C 言語でマクロを使ってコードを生成していたのを思い出した。

そんなわけで。マクロを使って「どう書く」のテストを実行するモジュールを書いてみた。

Elixir のドキュメントにテストの説明があったので、だいたいそのまま。

テストランナ

結果判定をする通常の関数 judgeuse されたときに実行される __using__ マクロ、個々のテストを定義する test マクロ、コンパイル時に実行される __before_compile__ マクロからなっています。

__before_compile__ が実行されるようにするには、@before_compile で指定する必要があるようです。

defmodule Doukaku.TestRunner do
  def judge(_, expected, expected) do
    IO.puts "\x1b[32mpassed\x1b[0m"
  end

  def judge(input, expected, actual) do
    IO.puts "\x1b[31mfailed  input: #{input}, expected: #{expected}, actual: #{actual}\x1b[0m"
  end

  defmacro __using__(_) do
    quote do
      import Doukaku.TestRunner
      @tests []
      @before_compile Doukaku.TestRunner
    end
  end

  defmacro test(input, expected) do
    quote do
      @tests [{unquote(input), unquote(expected)}|@tests]
    end
  end

  defmacro __before_compile__(_) do
    quote do
      def run do
        @tests
        |> Enum.reverse()
        |> Enum.each(fn {input, expected} -> judge(input, expected, solve(input)) end)
      end
    end
  end
end

どう書いたコードの例

英字の小文字だったばあいに大文字に変換するとかそういうの。

テストを定義する部分は CapitarlTest などに分離するのがきれいなのかもしれません。

defmodule Capitarl do
  use Doukaku.TestRunner

  def to_capital(c) do
    c - 32
  end

  def solve(input) do
    input
    |> String.to_charlist
    |> Enum.map(&to_capital/1)
    |> List.to_string
  end

  test("a", "A")
  test("ab", "AB")
  test("Ab", "AB")
end

Capitarl.run

実行

本当はパッケージにまとめておくのが正解の気がしますが、今回はファイル 2 つだけですまそうと。

テストランナをコンパイルします。

$ elixirc doukaku/test_runner.ex

どう書いたコードを実行します。

$ elixir capital.exs 
passed
passed
failed  input: Ab, expected: AB, actual: !B

いつか読むはずっと読まない:どこかで聞いた気がする

懐かしいとはちょっとちがう、どこかで聞いた気がする、という感じが集まった一冊。

一番にそれを感じたのが最後の防火水槽のくだり。

里山奇談

里山奇談

なお、「今日の早川さん」の第 4 集が控えているらしい。

今日の早川さん (早川書房)

今日の早川さん (早川書房)

移転しています

はてなダイアリー」が役目を終えつつあるということで重い腰を上げて移転作業をしています。

わたしのブログは「ダイアリー」に書いているにもかかわらず、にポストする記事は月一回ペースの事実上「マンスリー」になっていて、看板に偽りあり状態だったのでちょうどよい機会なのかもしれません。

実はブログ移転はこれで 2 度め。2007/05/18ココログから移転しています。それからほぼ 10 年。

あたらしい記事を書く場所は 新ブログ に移転しますが 旧ブログ もしばらくそのままにしておこうと思います。

いつか読むはずっと読まない:すばらしい新ブログ

ディストピア小説フェアに乗っかってます。半年ぐらいずれてる気もしますが。

矩形の中の矩形

オフラインリアルタイムどう書くE14 の中で、@yancya さんが書かれたターゲットとなる矩形(配列の配列)を取り出すコードがスマートだったので、Prolog で書いてみました。

Ruby

今回の問題の特徴から解くために向きは影響しないため、解答のコードでは transposeは 1 回ですが、下記の例では向きを元に戻すため再度 transpose しています。

rect = %w(12345 23456 34567 45678).map(&:chars).map {|row| row.map(&:to_i) }
# => [[1, 2, 3, 4, 5], [2, 3, 4, 5, 6], [3, 4, 5, 6, 7], [4, 5, 6, 7, 8]]
rect.each_cons(3).map {|rows| rows.transpose.each_cons(3).map(&:transpose) }.flatten(1)
# => [[[1, 2, 3], [2, 3, 4], [3, 4, 5]],
#     [[2, 3, 4], [3, 4, 5], [4, 5, 6]],
#     [[3, 4, 5], [4, 5, 6], [5, 6, 7]],
#     [[2, 3, 4], [3, 4, 5], [4, 5, 6]],
#     [[3, 4, 5], [4, 5, 6], [5, 6, 7]],
#     [[4, 5, 6], [5, 6, 7], [6, 7, 8]]]


Prolog

GNU Prolog で実装しています。

extract(Offset, Length, List, Extract) :-
  append(Left, ExtractRight, List),
  append(Extract, _Right, ExtractRight),
  length(Left, Offset),
  length(Extract, Length).

subrectangle(Width, Height, Rectangle, SubRectangle) :-
  extract(_, Height, Rectangle, Rows),
  maplist(extract(_, Width), Rows, SubRectangle).


subrectangle.prolog という名前で保存して、gprolog コマンドで GNU Prolog を起動します。

| ?- ['subrectangle.prolog'].
| ?- findall(S, subrectangle(3, 3, [[1,2,3,4,5],[2,3,4,5,6],[3,4,5,6,7],[4,5,6,7,8]], S), SS).

SS = [[[1,2,3],[2,3,4],[3,4,5]],[[2,3,4],[3,4,5],[4,5,6]],[[3,4,5],[4,5,6],[5,6,7]],[[2,3,4],[3,4,5],[4,5,6]],[[3,4,5],[4,5,6],[5,6,7]],[[4,5,6],[5,6,7],[6,7,8]]]


例えば、3 x 3 のリストのリストの要素の合計を表す述語を用意しておくと、

sum3x3([[A11, A12, A13], [A21, A22, A23], [A31, A32, A33]], Sum) :-
  Sum is A11 + A12 + A13 + A21 + A22 + A23 + A31 + A32 + A33.


それを満たす組み合わせを得ることができます。

| ?- subrectangle(3, 3, [[1,2,3,4,5],[2,3,4,5,6],[3,4,5,6,7],[4,5,6,7,8]], S), sum3x3(S, 45), !.

S = [[3,4,5],[4,5,6],[5,6,7]]


次回はどう書くの問題を解くコードを Prolog で書きます。つづく。

いつか読むはずっと読まない:かはく

国立科学博物館、通称かはく

始祖鳥・ロンドン標本が来ています(2017/06/11 まで)。

かはくは一度入ると3、4時間でてこられなくなるので危険です。


国立科学博物館のひみつ

国立科学博物館のひみつ

国立科学博物館のひみつ 地球館探検編

国立科学博物館のひみつ 地球館探検編

第3回ESMどう書くを開催しました

前回開催からおよそ1年半。第3回社内向けどう書くを開催しました。

プログラミングElixir

現在、社内で「プログラミングElixir」の読書会を開催しています。

プログラミングElixir

プログラミングElixir

文法周りが一通り終わったところで、じゃぁ何か書いてみようと、開催したもの。Elixirオンリーという変則どう書く。

とはいえ不慣れな言語では一時間で書けるコードにも限界があるし、かといって一時間で書ける程度の問題ではあまり面白くない。変則レギュレーションというならと、時間設定も変則にして出題から解答発表まで一週間の時間をとりました。「オフラインリアルタイムどう書く」とは別の形の「どう書く」でした。

そして問題

こちらが出題した問題です。


今回はたっぷりと1週間も時間をとったのだからと…

…と私が好きなものを色々混ぜてみました。それだけでなくて、

  • 言語レベルでバイナリ操作ができる
  • エンコード、デコードにパタンマッチが使える
  • 再帰構造

と、Elixir で表現するにはうってつけだと想像、あるいは妄想しての出題。


ちなみにカルキュラマシーンという名前に反応した方は昭和40年代生まれの可能性が高いです。

閑話休題


今回はElixirで書くことに注目していたので、手順自体はルールに書いたことがほぼすべて。これを Elixir でどのように表現するかが実質の課題でした。

  1. 十六進数をビット列や 0/1 のリストにする
  2. パタンマッチを使ってデコードする
  3. トークン(オペランドとオペレータ)に分解
  4. 計算可能な構造を構築
  5. 計算
  6. 結果の数値のデコード
  7. 解答文字列の構築

予想外に難題だったビット列取り出し

デコードや演算処理で頭を悩ませることは想像していたのですが、最初のビット列の取り出しでつまずく人多数。

たとえば、1桁の16進数を4桁の2進数に変換する場合。2進数にしたときに最上位の桁が1であれば [https://hexdocs.pm/elixir/Integer.html#to_string/2:title=Integer.to_string][https://hexdocs.pm/elixir/Integer.html#digits/2:title=Integer.digits] を使って簡単に4桁の2進数を作れるのですが、

Integer.to_string(String.to_integer("c", 16), 2) # => "1100"
Integer.digits(String.to_integer("c", 16), 2) # => [1, 1, 0, 0]

上位の桁が0のばあい、単純な変換ではそれらの桁が落ちてしまいます。

Integer.to_string(String.to_integer("4", 16), 2) # => "100"
Integer.digits(String.to_integer("4", 16), 2) # => [1, 0, 0]

上位に気をつけないとうまく取り出せない0の桁がある、下位にパディングとして付加された利用しない0がある、の二つが重なって難儀することになったようです。

そして今回も出題不備が…

今回の問題の中には冪演算が登場するのですが、Elixir には冪演算の演算子や関数がありません。そこでつい親切心を出して Erlangmath ライブラリをヒントとして紹介したのですが、これが解答者を混乱させる結果に。

math ライブラリにある pow 関数は戻り値が浮動小数点数です。求めたい値が整数値の整数値乗だとしても浮動小数点数の値として返ってきます。そして浮動小数点数には精度というものがって、今回の問題中の冪演算をすると精度がたりなくて正しい値にならないのでした。

出題内容と別のところで解答者を惑わすという失態。


問題は、アイディアが降りてくれば作ること自体はすごく難しいというわけではないのですが、それをきちんと解答者に伝えるようにすることは、相変わらず難しい…。


第4回もやります

今回は学習中の一つの言語に絞ってみんなで書いてみたことで、書き方、ライブラリの使い方、もろもろ学ぶことができました。
だいぶ変則的な開催となりましたが得られたものは多かった気がします。

レギュレーションがどうなるかはわかりませんが、次もやる予定です。


…出題の詰めの甘さをどうにかしたい。

いつか読むはずっと読まない:バイナリのたのしみ

Elixirはバイナリの扱いが得意ですが、それならばこれ。もう何度もこのブログで紹介していますが、この本は楽しいです。

ハッカーのたのしみ―本物のプログラマはいかにして問題を解くか

ハッカーのたのしみ―本物のプログラマはいかにして問題を解くか

  • 作者: ジュニア,ヘンリー・S.ウォーレン,Jr.,Henry S. Warren,滝沢徹,玉井浩,鈴木貢,赤池英夫,葛毅,藤波順久
  • 出版社/メーカー: エスアイビーアクセス
  • 発売日: 2004/09
  • メディア: 単行本
  • 購入: 35人 クリック: 732回
  • この商品を含むブログ (128件) を見る


しばらく入手困難な状態でしたが、現在はオンデマンド版で入手可能なようです。

ハッカーのたのしみ―本物のプログラマはいかにして問題を解くか

ハッカーのたのしみ―本物のプログラマはいかにして問題を解くか

  • 作者: ジュニア,ヘンリー・S.ウォーレン,Jr.,Henry S. Warren,滝沢徹,鈴木貢,赤池英夫,葛毅,藤波順久
  • 出版社/メーカー: エスアイビーアクセス
  • 発売日: 2015/01
  • メディア: 単行本
  • この商品を含むブログを見る