読者です 読者をやめる 読者になる 読者になる

nesheep5's blog

プログラミング・子育て・音楽 etc...

エンジニアのヘルスケアで本当に買ってよかったもの5選

プログラミング 健康

エンジニアになってから肩こり・腰痛・腱鞘炎、、、など満身創痍になり、ヘルスケアのために日々試行錯誤しています。 その中でもこれは買ってよかった!と思えるものを紹介します。

LPN ストレッチポール(R)EX

LPN ストレッチポール(R)EX ネイビー 0001
LPN
売り上げランキング: 73

ラグビーをやっているエンジニアの方に薦めてもらいました。
ポールの上に寝そべり、エクササイズをすることで正しい姿勢に矯正することができます。
プロのアスリートも多く使われているそうです。
特に猫背の矯正、肩甲骨まわりのストレッチに効果があり、デスクワークで凝り固まった体をほぐすことができます。
類似品が色々ありますが、正規品は作りがしっかりしていてヘタりもなく硬さも絶妙だったのでおすすめです。

ドクターエア 3Dマッサージロール

とあることからモニターをやることになりこの商品を知りました。
3段階のバイブレーションで凝っている箇所をほぐします。充電式でとにかく手軽につかえるので、テレビをみながらソファーで使用したりと、「買ったけど面倒になって使わない」がなく毎日使用しています。
バイブレーションだけでコリが取れるのか?と半信半疑だったのですが、筋肉を振動させることで筋膜剥がしのような効果があるそうです。足などに使うとむくみがとれ、柔軟性も高まります。
モミ玉でゴリゴリやったときのようなもみ返しがないのもメリットです。

PFU Happy Hacking Keyboard Professional2 墨

MacBookを使うようになってから薄いキーボードのせいで指が痛むようになったため、購入しました。
静電容量無接点方式のためキーがとても軽く底打ちせずに入力できるので、指や手首への負担が格段に減りました。
また矢印キーもないほどコンパクトなので、マウスへ手を伸ばす距離が最小限になり、腕への負担も少なくなります。
知る人ぞ知る高級キーボードで購入までかなり悩みましたが、本当に買ってよかったと思える一品です。

無印良品 ネッククッション

昼休みに15分ほど仮眠を取っているのですが、良い枕がないか探したところこれにたどり着きました。
中身がサラサラのビーズクッションになっているため、寝る姿勢に合わせて形を変えることができます。
使う前は起きた時に首が痛かったのですが、使ってからは短時間でしっかり体を休めることが出来るようになりました。

MOLDEX 耳栓お試し8種類セット

MOLDEX 耳栓お試し8種類セット
Moldex
売り上げランキング: 4,026

集中したい時、仮眠を取るときに使用するため、いろんな耳栓を試してこれにたどり着きました。
遮音性が抜群で、人の声もきちんとカットできます。(逆に声をかけられても反応できないくらいです)
大きさや柔らかさ、遮音特性などが異なる8種がセットになっているので、自分に合うものが見つけられると思います。
個人的にはカモフラージュ柄のカモプラグがおすすめです。

すぐできる!Java100本ノックをSlackに自動投稿するBot

プログラミング

はじめに

先日、JJUG CCC 2016 Fallに参加してきました。 その中でJava100本ノックが紹介されており、面白そうだから社内でもやってみようかという話になりました。 Slackに問題を自動投稿するBotスプレッドシートとGASで作ってみたので、よければ参考にしてみてください。

手順

1.問題番号を保存するスプレッドシート作成

Java100本ノックSlackBot_-_Google_スプレッドシート.png

2.スクリプトエディタを開く

スプレッドシートのメニューから「ツール」→「スクリプトエディタ」を選択します。

3.GASにSlackAppライブラリを導入する

スクリプトエディタのメニューから「リソース」→「ライブラリ」を選択し、以下のLibrary Keyを入力します。

SlackAppのLibrary Key → M3W5Ut3Q39AaIwLquryEPMwV62A3znfOO

4.Slackのトークンを発行

こちらの記事を参考に、Slackのトークンを発行します。

5.コード作成

スクリプトエディタに以下のコードを貼り付けます。 TOKEN定数は上記で取得したトークンに置き換えてください。

var URL = "https://github.com/JustSystems/java-100practices/tree/master/contents/";
var TOKEN = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; // 取得したトークンを貼り付け

function main() {
  // 平日のみ投稿
  var today = new Date();
  if(isJapaneseHoliday(today) || today.getDay() == 0 ||today.getDay() == 6){return;} 
  
  var qNumRange = SpreadsheetApp.getActiveSheet().getRange(1, 2);
  var qNum = qNumRange.getValue();
  postSlackMessage(qNum);
  
  // 問題インクリメント
  qNum++;
  if(qNum > 100){ qNum = 1;}
  qNumRange.setValue(qNum);
}


function postSlackMessage(qNum) {
  var slackApp = SlackApp.create(TOKEN); //SlackApp インスタンスの取得
 
  var options = {
    channelId: "#java100knock",
    userName: "Java100本ノックBot",
    message: "今日の問題はこちら!\n" + 
             URL + ('000' + qNum).slice( -3 ) // 問題番号0埋め         
  };
 
  slackApp.postMessage(options.channelId, options.message, {username: options.userName});
}

function isJapaneseHoliday(date) {
  var year = date.getFullYear();
  var month = date.getMonth();
  var day = date.getDate();
  
  var startDate = new Date();
  startDate.setFullYear(year, month-1, day);
  startDate.setHours(0, 0, 0, 0);

  var endDate = new Date();
  endDate.setFullYear(year, month-1, day);
  endDate.setHours(23, 59, 59, 999);

  var cal = CalendarApp.getCalendarById("ja.japanese#holiday@group.v.calendar.google.com");
  var holidays =  cal.getEvents(startDate, endDate);

  return holidays.length != 0;
}

この状態でmainメソッドを実行すると、Slackのjava100knockチャンネルに問題が投稿されます。

6.起動トリガーを設定

スクリプトエディタのトリガーアイコン(時計のマーク)より、トリガーを設定します。 Java100Knock.png

この設定で毎朝8~9時に問題が自動投稿されます。(ソース内で平日のみ投稿するように制御しています)

投稿された問題は以下のような感じになります。 Slack_-_Paygent.png

参考

Scala入門時に役立つ情報まとめ

プログラミング

はじめに

Scalaの勉強を始めた時にJavaRubyと比べると情報量が少なく苦戦したので、今まで調べたことや経験者から聞いた情報などをまとめてみようと思います。 私自身まだまだ初心者ですが、これからScalaやってみようかなと思っている人の参考になれば幸いです。

WEBサイトで勉強する

ScalaのインストールやHelloWorldなどは検索するとすぐ見つかるので割愛します。 Scala特有の記法や概念などを勉強するのに以下のサイトが参考になりました。

技術系

読み物系

勉強会・イベントに参加する

技術書選びやネットでの勉強が意外と苦戦したので、勉強会を探して経験者に直接聞く作戦に切り替えました。ただ勉強会も他の言語に比べると少ないです。 関東で唯一?定期開催している芸者東京エンターテインメント社の「Scala勉強会in東京」にお邪魔させていただきました。 結果として作戦は大成功で、有益な情報がたくさん聞けました。可能な限り通いたいと思います。 またScalaをテーマにした日本最大級のカンファレンス「ScalaMatsuri」が2016年1月30、31日に開催されるので参加してみようと思います。

書籍で勉強する

通称コップ本と呼ばれる「Scalaスケーラブルプログラミング第2版 (2016/09/20に第3版が出版されました!)」がいちばん有名です。ただし全35章、700ページ超えと、初心者が読み切るにはそれなりの覚悟が必要です。苦笑

業務でScala使っている方に伺ったところ、普段は「Scala逆引きレシピ」で調べて、深く知りたい箇所をコップ本で勉強するのが良いのでは。との回答でした。 個人的には「関数プログラミング実践入門」「Javaによる関数型プログラミング」を読んでいたおかげで比較的すんなり頭に入ってきた感があるので、関数型プログラミングを全く知らない方は合わせて読むと良いかも知れません。

※ 参考:Scalaを学ぶためのScala本の読み進め方 (10冊)

問題を解く

とにかくコード書くのが上達の近道とのことで、別言語技術書のサンプルソースScalaに書き直す、などやったりするそうです。 最近はオンラインでプログラミングの問題を解くことができるので、簡単なレベルのものからScalaで書いています。プロコン系は開催日が指定されていることが多いので、自分のペースで進められる「Paiza」「AOJ」あたりがオススメです。 また「AtCoder」は過去問について他ユーザの回答を閲覧できるので、自力で解いた後に他の回答を見て良いところを盗む、といった使い方ができます。

Scala界の著名エンジニアを追う

Scalaの最新情報をキャッチアップする方法について勉強会で相談したところ、口を揃えて「吉田さんをフォローしろ」と言われました。 てっきりどこかのサイトを教えてもらえるものだと思っていたので、エンジニアをフォローするというアプローチはかなり新鮮でした。 私が参考にさせてもらっているエンジニアの方たちを挙げさせてもらいます。(50音順)

瀬良 和弘さん

エムスリー社ソフトウェアエンジニア。 ScalikeJDBC、Skinny Framework など Scala OSS プロジェクトリード。 Scalatra、json4s、Scalate のメンテナ。

竹添 直樹さん

ビズリーチ社チーフアーキテクト。 Scala逆引きレシピ著者、GitBucketファウンダー。

中村 学さん(がくぞさん)

株式会社Tech to Value 代表取締役。 Japan Scala Association理事。

水島 宏太さん

ドワンゴプログラマ。 Japan Scala Association代表理事。

Kenji Yoshidaさん

scalazとsbtとargonautとscalikejdbcのコミッター。

フレームワーク関連

WEBアプリフレームワーク

Play Framework」がデファクトスタンダードと言ってよさそうです。執筆時点で安定版である2.4系の情報が意外と少ないので、古い記事は公式の移行ガイドなどを見ながら読み替えをする必要になるかも知れません。ビズリーチ社のハンズオンは2.3系、2.4系が用意されているのでとても参考になりました。 シンプルでSinatraライクなWebフレームワークとして「Scalatra」も人気があるようです。

DBアクセスライブラリ

SlickScalikeJDBCが二大勢力のようです。SlickはPlayFrameworkの標準ライブラリになりました。 ScalikeJDBCは前述のエンジニアの方たちが作っているので、本当に困ったら直接聞けるというメリットがあります。(と勉強会で教わりました。笑) 私個人はScalikeJDBCを勉強し始めた段階なので、それぞれの良さがわかった頃に改めてまとめたいと思います。

※ 参考:Slick vs ScalikeJDBC

その他

scalazを使うエンジニアを業界では「Z戦士」と呼ぶそうです。笑 * scalaz * 独習 Scalaz

開発環境関連

IntelliJ IDEA

PlayFrameworkの勉強時、始めはEclipseにScalaIDEというプラグインを入れて使っていたのですが、動作が不安定&重いのでIntelliJ IDEAに乗り換えてみました。まだ使い始めなので慣れないですが、サクサク動いておすすめです。

ATOM

最近注目のテキストエディタATOMを使って開発環境を整えることもできるそうです。IDEを使うには大げさすぎる時など状況によって使い分けるといいかもしれません。 * AtomでのScala開発環境 - たけぞう瀕死ブログ

小ネタ

REPLでシンタックスハイライト

  • Scala 2.11.4より、ターミナルで"scala -Dscala.color"と入力してREPLを起動するとちょっとだけ(REPL結果の変数と型だけ)カラー表示となる。

Java8 逆引き Stream API

プログラミング

はじめに

Java8の新機能、Stream APIを使い始めてコーディングが格段に楽になりました。 備忘録的にサンプルコードを書き出してみました。少しずつ追加していく予定です。 Javaは使ったことあるけどラムダ式やStream APIはよく知らないという方は、参考にしてみてください。 バリバリ使っている方は、こんな使い方もあるよ!と教えていただけると嬉しいです。

ちなみにオライリー社のJavaによる関数型プログラミング ―Java 8ラムダ式とStreamがとても参考になりました。

【前提条件】Stream APIの説明に使用するオブジェクト

public class Person {
  private String name;
  private int age;
  private List<Person> children;

  public Person(String name){/*...*/}
  public Person(String name, int age){/*...*/}
  public Person(String name, int age, Person... children){/*...*/}
  
  @Override
  public String toString() {/* name(age) [children...] */}
  
  // getter, setter...
}

コレクション、配列、MapをStreamクラスに変換(Arrays#stream,Stream#of)

  // コレクション
  List<String> list = new ArrayList<>();
  Stream<String> listStream = list.stream();

  // 配列
  String[] ary = new String[] {"AA","BB","CC"};
  Stream<String> aryStream1 = Arrays.stream(ary);
  Stream<String> aryStream2 = Stream.of(ary);

  // MAP
  Map<String,String> map = new HashMap<>();
  Stream<Entry<String, String>> mapStream = map.entrySet().stream();

メソッド参照・コンストラクタ参照(Class::method)

  String[] names = new String[] {"Tom","Bob","Alice"};

    Stream.of(names)
      // コンストラクタ参照
      .map(Person::new) // name -> new Person(name)
      // メソッド参照
      .forEach(System.out::println); // person -> System.out.println(person)
      // Tom(0)
      // Bob(0)
      // Alice(0)

Personリストからnameリスト作成(Stream#collect,Collectors#toList)

  List<Person> persons = new ArrayList<>();
  persons.add(new Person("Tom",  21));
  persons.add(new Person("Bob",  25));
  persons.add(new Person("Alice",19));

  List<String> nameList =
    persons.stream()
      .map(Person::getName)
      .collect(Collectors.toList());
 
  nameList.steram().forEach(System.out::println);  
    // "Tom"
    // "Bob"
    // "Alice"

PersonリストからnameのCSV作成(Stream#collect,Collectors#joining)

  List<Person> persons = new ArrayList<>();
  persons.add(new Person("Tom",  21));
  persons.add(new Person("Bob",  25));
  persons.add(new Person("Alice",19));

  String nameCSV =
    persons.stream()
      .map(p -> String.format("\"%s\"", p.getName()))
      .collect(Collectors.joining(","));

  System.out.println(nameCSV);
  // "Tom","Bob","Alice"

Personリストからageの重複なしリスト作成(Stream#distinct)

  List<Person> persons = new ArrayList<>();
  persons.add(new Person("Tom",  21));
  persons.add(new Person("Bob",  25));
  persons.add(new Person("Alice",19));
  persons.add(new Person("Mike", 19));

  List<Integer> ageDistinctList =
    persons.stream()
      .map(Person::getAge)
      .distinct()
      .collect(Collectors.toList());
    
  ageDistinctList.stream().forEach(System.out::println);
  // 21
  // 25
  // 19

Personリストをage順にソート(Stream#sorted,Comparator#comparingInt)

  List<Person> persons = new ArrayList<>();
  persons.add(new Person("Tom",  21));
  persons.add(new Person("Bob",  25));
  persons.add(new Person("Alice",19));

  persons.stream()
    .sorted(Comparator.comparingInt(Person::getAge))
    .forEach(System.out::println);
    // Alice(19)
    // Tom(21)
    // Bob(25)
    

Personリストをname頭文字でグループ化(Comparator#groupingBy)

  List<Person> persons = new ArrayList<>();
  persons.add(new Person("Tom",  21));
  persons.add(new Person("John", 18));
  persons.add(new Person("Jack", 19));

  Map<Object,List<Person>> nameIndex =
    persons.stream()
      .collect(Collectors.groupingBy(p -> p.getName().charAt(0)));

    System.out.println(nameIndex);
    // {J=[John(18), Jack(19)], T=[Tom(21)]}

Personリストからchildrenリスト作成(Stream#flatMap)

  List<Person> persons = new ArrayList<>();
  persons.add(new Person("Tom", 21,
                  new Person("Tomas", 1), 
                  new Person("Tommy", 0)));
  persons.add(new Person("Bob", 25,
                  new Person("Bobby", 2)));
  persons.add(new Person("Alice", 19));

  persons.stream()
    .flatMap(person -> person.getChildren().stream())
    .forEach(System.out::println);
    // Tomas(1)
    // Tommy(0)
    // Bobby(2)    

try-with-resourcesでリソース解放されないパターン

プログラミング

Java7から"try-with-resources"構文が追加されました。 ファイルやDBアクセスしたあとのリソース解放を自動で行ってくれる大変便利な機能で、解放し忘れをなくし、コードをすっきりさせることができます。 ただし、書き方によってリソースが解放されないパターンがあったので紹介します。

具体的には以下のような場合です。 リソース解放の対象クラスをネストさせてインスタンス生成した場合、コンストラクタで例外が発生するとリソース解放されません。

File file = new File("out.txt");
// PrintWriterがインスタンス生成に失敗すると、BufferedWriter・FileWriterが解放されない
try(PrintWriter pw = 
        new PrintWriter(new BufferedWriter(new FileWriter(file)));) {
    // 処理
}
// ・・・

検証

各Writerクラスにログを仕込み、どのような動作をするか検証してみました。

public class Test {
    public static void main(String[] args) {

        File file = new File("out.txt");
        try (PrintWriter pw = 
                new PrintWriterWrapper(
                new BufferedWriterWrapper(
                new FileWriterWrapper(file)));) {
            System.out.println("func");
        } catch (Exception e) {
            System.out.println("catch:" + e);
        } finally {
            System.out.println("finally");
        }
    }

    // 以下、ログを追加したラッパークラス

    public static class PrintWriterWrapper extends PrintWriter {

        public PrintWriterWrapper(Writer out) {
            super(out);
            System.out.println("new PrintWriter");
        }

        @Override
        public void close() {
            System.out.println("close PrintWriter");
            super.close();
        }
    }

    public static class BufferedWriterWrapper extends BufferedWriter {

        public BufferedWriterWrapper(Writer out) {
            super(out);
            System.out.println("new BufferedWriter");
            throw new RuntimeException();
        }
        @Override
        public void close() throws IOException {
            System.out.println("close BufferedWriter");
            super.close();
        }
    }

    public static class FileWriterWrapper extends FileWriter {

        public FileWriterWrapper(File file) throws IOException {
            super(file);
            System.out.println("new FileWriter");
        }

        @Override
        public void close() throws IOException {
            System.out.println("close FileWriter");
            super.close();
        }
    }
}

処理が正常終了する場合、作成したインスタンスを逆順でリソース解放(closeメソッド実行)しています。

// ・・・
// ネストでインスタンス生成する
try (PrintWriter pw = 
        new PrintWriterWrapper(
        new BufferedWriterWrapper(
        new FileWriterWrapper(file)));) {
    System.out.println("func");
}
// ・・・

// 実行結果(close()が実行されている)
// new FileWriter
// new BufferedWriter
// new PrintWriter
// func
// close PrintWriter
// close BufferedWriter
// close FileWriter
// finally

ただしコンストラクタで例外が発生した場合、内包するインスタンスに対するリソース解放がされません。

// ・・・
// ネストでインスタンス生成する
try (PrintWriter pw = 
    new PrintWriterWrapper(
    new BufferedWriterWrapper(
    new FileWriterWrapper(file)));) {
  System.out.println("func");
}

// ・・・

// PrintWriterWrapperのコンストラクタで例外発生させる
public PrintWriterWrapper(Writer out) {
    super(out);
    System.out.println("ERROR!! new PrintWriter");
    thorow new RuntimeException();
} // ・・・

// 実行結果(close()が実行されない)
// new FileWriter
// new BufferedWriter
// ERROR!! new PrintWriter
// catch:java.lang.RuntimeException
// finally

ポイントは以下の2点です。

  • "try句で変数として宣言されたインスタンス"が自動リソース解放の対象となる
  • try句でインスタンス生成する際、コンストラクタで例外が発生した場合はcloseメソッドが実行されない

検証1では、変数pwのcloseメソッドが実行され、内包するBufferedWriter、FileWriterを連鎖的にcloseしています。 検証2では変数pwのcloseメソッドが実行されず、内包するインスタンスも自動リソース解放の対象となっていないためそのまま残ってしまいます。

解決法:ネストせず個別に変数定義する

結論として、コンストラクタで例外が発生しないことが明白である場合以外は個別に変数定義するのが良さそうです。 以下の例ではPrintWriterのインスタンス生成に失敗した場合もBufferedWriter、FileWriterのcloseメソッドが実行されています。 (FileWriterのcloseメソッドが2回実行されているのは、BufferedWriterのcloseメソッドから連鎖的に実行されたのと変数fwとして宣言したため自動リソース解放の対象となっているためです。)

// ・・・
// 個別にフィールドを宣言し、それぞれインスタンス生成する
try ( FileWriter fw = new FileWriterWrapper(file);
    BufferedWriter bw = new BufferedWriterWrapper(fw);
    PrintWriter pw = new PrintWriterWrapper(bw);) {
  System.out.println("func");
}

// ・・・

// PrintWriterWrapperのコンストラクタで例外発生させる
public PrintWriterWrapper(Writer out) {
    super(out);
    System.out.println("ERROR!! new PrintWriter");
    thorow new RuntimeException();
}
// ・・・

// 実行結果(close()が実行されている)
// new FileWriter
// new BufferedWriter
// ERROR!! new PrintWriter
// close BufferedWriter
// close FileWriter
// close FileWriter
// catch:java.lang.RuntimeException
// finally