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

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

Duck Typing 〜 Elixirの多態

# 鶩
defmodule Duck do
  defstruct name: nil
end

# 犬
defmodule Dog do
  defstruct name: nil
end

# 猫
defmodule Cat do
  defstruct name: nil
end

# 鳴くプロトタイプ
defprotocol Sound do
  def sound(_)
end

# 鶩が鳴く実装
defimpl Sound, for: Duck do
  def sound(animal) do
    IO.puts "#{animal.name} the Duck sounds quack quack."
  end
end

# 犬が鳴く実装
defimpl Sound, for: Dog do
  def sound(animal) do
    IO.puts "#{animal.name} the Dog sounds bow-wow."
  end
end

# 猫が鳴く実装
defimpl Sound, for: Cat do
  def sound(animal) do
    IO.puts "#{animal.name} the Cat sounds miaow miaow."
  end
end

defmodule Test do
  duck = %Duck{name: "Donald"} # ドナルド
  dog = %Dog{name: "Shiro"}    # しろ
  cat = %Cat{name: "Tama"}     # たま

  Sound.sound(duck)
  Sound.sound(dog)
  Sound.sound(cat)
end

実行。

$ elixir sound.ex 
Donald the Duck sounds quack quack.
Shiro the Dog sounds bow-wow.
Tama the Cat sounds miaow miaow.

Amazon CloudWatch Logs からログを取得する gem を書いた

AWS に CloudWatch Logs というサービスがあります。

コンソールが用意されていますが、正直使いやすくありません。

awslogs という、CloudWatch Logs からログを取得するコマンドラインツールがあり使っていたいのですが、だんだんと不満な点が出てきました。

そんなわけなので

CloudWatch Logs からログを取得する gem を書きました。

実体は aws-sdk gem に薄い層を被せたものです。

使う

ここでの例は Thor gem を使ってコマンドにしたものです。

Gemfile

source 'https://rubygems.org'

git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }

gem 'aws_cloudwatch_logs', github: 'mattsan/aws_cloudwatch_logs'
gem 'thor'

command

#!/usr/bin/env ruby

require 'thor'
require 'time'
require 'aws_cloudwatch_logs'

class Logs < Thor
  default_command :logs

  desc :logs, 'get logs'
  option :start, aliases: '-s', type: :string, desc: 'start time'
  option :end, aliases: '-e', type: :string, desc: 'end time'
  option :group, aliases: '-g', type: :string, desc: 'log group'
  option :filter, aliases: '-f', type: :string, desc: 'filter pattern'
  def logs
    start_time = Time.parse(options[:start])
    end_time = Time.parse(options[:end])
    log_group = options[:group]
    filter_pattern = options[:filter]

    AwsCloudwatchLogs.extract(log_group, start_time, end_time, filter_pattern) do |event|
      time_string = event.timestamp.strftime('%Y-%m-%dT%H:%M:%S%Z')
      puts "#{time_string} #{event.message}"
    end
  end
end

Logs.start

logs というファイル名で保存して実行できるようにします。

$ chmod +x logs

実行

$ bundle exec logs -g foo-bar-baz -s '2017/11/01 00:00' -e '2017/11/01 12:00'

このように aws-sdk を使うときのメンドクサイ部分を隠しただけのものですがメンドクサイ部分を繰り返す必要がないだけ楽になります。

そんな感じで。

複数のテキストファイルの内容を横に並べて出力する

複数のテキストファイルの各行を横に並べて表示したいとき、たとえば次のような abc.txt と 123.txt というファイルがあったとき、

abc.txt:

A
B
C

123.txt

1
2
3

次のようにファイルディスクリプタを指定してリダイレクトすると同時に読み込めるので、あとは一行に並べて出力するとできる模様。

while read -u 4 a && read -u 5 n
do
  echo $a $n
done 4< abc.txt 5< 123.txt

結果。

A 1
B 2
C 3

一行野郎はこんな感じ。

while read -u 4 a && read -u 5 n; do echo $a $n; done 4< abc.txt 5< 123.txt

参考にした Stack Overflow: How to read from two files in the same time shell - Stack Overflow

前日の日付を取得する

#!/bin/bash

# 時刻を引数で指定されなかった場合に、現在時刻から前日の日付と今日の日付を取得する
#
# 日付を取得するのであれば `date` コマンドを使うのがよいのだけれど、
# オプションに差があるので使い慣れた Ruby に逃げた。
#
# | 系統 | コマンド                      |
# |------|-------------------------------|
# | GNU  | `date -d '1 day ago' +%Y%m%d` |
# | BSD  | `date -v-1d +%Y%m%d`          |

if [ "$1" = "" ]; then
  START_TIME=$(ruby -e 'puts (Time.now - 24 * 60 * 60).strftime("%Y/%m/%d 06:00")')
  END_TIME=$(ruby -e 'puts Time.now.strftime("%Y/%m/%d 06:00")')
else
  START_TIME=$1
  END_TIME=$2
fi

echo start $START_TIME
echo end   $END_TIME

Io FizzBuzz

昨日のFizzBuzz のつづき。

再掲。

久しぶりに Io でコードを書いたら、すっかり作法を忘れてしまっていることに気がつきました。if 文の書き方、期待する動きになるように書けたはいいのだけれど、こんな書き方でよかったのか未だにこころもとない。

Number fizz := method(
  if(self % 3 == 0,
    result := "Fizz"
    result n := self
    result,
    self
  )
)

Number buzz := method(
  if(self % 5 == 0,
    result := "Buzz"
    result n := self
    result,
    self
  )
)

Sequence fizz := method(
  if(self n % 3 == 0,
    result := self .. "Fizz"
    result n := self n
    result,
    self
  )
)

Sequence buzz := method(
  if(self n % 5 == 0,
    result := self .. "Buzz"
    result n := self n
    result,
    self
  )
)

 1 fizz println
 2 fizz println
 3 fizz println
 5 fizz println
 9 fizz println
10 fizz println
12 fizz println
15 fizz println

"---" println

 1 buzz println
 2 buzz println
 3 buzz println
 5 buzz println
 9 buzz println
10 buzz println
12 buzz println
15 buzz println

"---" println

 1 fizz buzz println
 2 fizz buzz println
 3 fizz buzz println
 5 fizz buzz println
 9 fizz buzz println
10 fizz buzz println
12 fizz buzz println
15 fizz buzz println

"---" println

 1 buzz fizz println
 2 buzz fizz println
 3 buzz fizz println
 5 buzz fizz println
 9 buzz fizz println
10 buzz fizz println
12 buzz fizz println
15 buzz fizz println

実行。

$ io fizzbuzz.io 
1
2
Fizz
5
Fizz
10
Fizz
Fizz
---
1
2
3
Buzz
9
Buzz
12
Buzz
---
1
2
Fizz
Buzz
Fizz
Buzz
Fizz
FizzBuzz
---
1
2
Fizz
Buzz
Fizz
Buzz
Fizz
BuzzFizz

Ruby や Io でならば簡単に実現できるこの FizzBuzz。簡単に実現できるその特徴とは。

いつか読むはずっと読まない:へびつかい座

読んで真っ先に思い出した作品がこれでした。

あるいはグレッグ・イーガン

解説にも、 「アイデンティティを揺るがせ、人間の定義を変容させていく行為が日常化した〈八世界〉は、サイバーパンクグレッグ・イーガン作品などの先駆に位置づけられるだろう」 とあり、ギミックは多少時代が感じられるものの、その物語は1970年代に書かれたとは思えない強烈さがあります。

C++で整数値を返したり文字列を返したりする

#include <string>
#include <iostream>

template<int N, bool is_string>
struct this_is_a_int_or_string;

template<int N>
struct this_is_a_int_or_string<N, false> {
    typedef int value_type;
    static const value_type value = N;
};

template<int N>
struct this_is_a_int_or_string<N, true> {
    typedef std::string value_type;
    static const value_type value;
};

template<int N>
const typename this_is_a_int_or_string<N, true>::value_type this_is_a_int_or_string<N, true>::value = "Fizz";

template<int N>
struct Fizz {
    typedef typename this_is_a_int_or_string<N, (N % 3) == 0>::value_type value_type;
    static const value_type value;
};

template<int N>
const typename Fizz<N>::value_type Fizz<N>::value = this_is_a_int_or_string<N, (N % 3) == 0>::value;

template<typename T>
std::string this_is(T);

template<>
std::string this_is(int n) {
    return "int";
}

template<>
std::string this_is(std::string s) {
    return "string";
}

int main(int, char* []) {
    std::cout << Fizz<1>::value << " is " << this_is(Fizz<1>::value) << std::endl;
    std::cout << Fizz<2>::value << " is " << this_is(Fizz<2>::value) << std::endl;
    std::cout << Fizz<3>::value << " is " << this_is(Fizz<3>::value) << std::endl;
    std::cout << Fizz<4>::value << " is " << this_is(Fizz<4>::value) << std::endl;

    return 0;
}

実行。

$ g++ -o this_is_a_int_or_string this_is_a_int_or_string.cpp 
$ ./this_is_a_int_or_string 
1 is int
2 is int
Fizz is string
4 is int

同じ Fizz<N>::value という形でありながら、N の値によって value の値が整数値であったり文字列であったりします。

FizzBuzzクイズ」の実現方法について @Nabetani さんと C++ で書ける書けないという話をした中で、「template でならできるかも」という流れになったので、書いてみました、途中まで。

本当はもう少し先まで書いたのですが…思うところがあって力尽きてしまいました。

「思うところ」は、また後ほど。

いつか読むはずっと読まない:クロニクル

ということなので。

火星年代記 (ハヤカワ文庫SF)

火星年代記 (ハヤカワ文庫SF)

パラダイスバード・クロニクル

パラダイスバード・クロニクル