よこなのへたのよこずき

noteもよろしくね

エンジニア立ち居振舞い:自分は出来ないと思い込みすぎない

お題「エンジニア立ち居振舞い」
↑便乗!!


自分は出来ない、レベルが低いと必要以上に思わない方がいいに違いない。

なぜ?

自分が困る

  • 精神的に疲れる
  • 他者からの評価すら下がる、給料アップチャンスを逃し得る
    いいと思っていないものをアピールするのって難しいからね。

後輩 *1 が困る

  • 適切な説明や指導が出来ない
    「自分に出来たのだから万人に出来るはず」「自分がバカだから分からなかったけど、この人には説明しなくても大丈夫」ってなる。
  • 後輩の自信や評価までもをマイナスにしてしまう

先輩 *2 が困る

  • 先輩だって間違える、ということを忘れてしまう
    先輩と意見が違ったとき、「自分の方が間違ってるんだろう」と思い込んで事前に分かったミスをスルーしてしまう。もっと良いはずのアイディアを出せない。

その他

  • 自分を評価してくれる人に失礼だ
    自分の成果を褒めてくれる人がいるのにそれを否定して嘘にしちゃうの、とても残念。

じゃあどうする?

  • 人ではなく、事実を評価しようとがんばる

    • バグ出した、知らないことがあった -> 自分はダメエンジニアだ
      この手の自己評価をして嘆いたところで、バグは直らなければ知識も増えない。
      「自分はまだまだレベルが低いから仕方ない」という逃げにしかならない。
      だから、
    • バグ出した -> 何故出した、どうやって直す、どうしたら防げた、次からどうする
    • 知らないことがあった -> 何が分からなかったのか、何を知ればいいのか、何を学んだのか + 何なら知っていたのか
      当たり前だけど、こういうことを考えて行動したい!
      自分の行動を、自分という人間の評価だとすぐに錯覚しない!
  • 時々自分の行動を褒める
    対象がなければ行動する。


おしまい!

*1:自分より経験が浅い人、年下の人、レベルが下の人、知ってることが少ない人…などを便宜上こう書いてみた

*2:自分より経験のある人、年上の人、レベルが上の人、知ってる人が多い人、所謂スゴイ人…などを便宜上こう書いてみた

うらがみさんアドバイス解析〜リテラルはGCされない〜 #hogedriven

この前関西のエンジニアおともだちと集まって遊んだ時に発表した資料を公開しちゃお〜


何かと言うとですね。
かなり昔だけど、ふと浮かんだ疑問をツイートしたら
うらがみ(@backpaper0)さんがコード書いて実験してくれて。
それを今更解析したよ〜ってことで発表して、内容合ってるか皆さんに見ていただいたの!
資料だけだと分かりづらいかもしれんけど、すごく勉強になったし面白かった。
うらがみさんへのお礼としてアップルパイも焼いていい経験になったよー!

ツイート↓

コード by うらがみさん↓
gist.github.com

この作業で生じたTODO

  • コンスタントプールについて勉強する…というかjavapを学ぶ
  • コードの後半も読んでみる

最後に、資料には書いてない感想を追加しておこう。
こうやって有識者の方に教えていただく内容って結構難しいけど、
ちゃんと考えれば理解できるし、何となく分かったで放置せず向き合っていきたいなー

ここ最近のおひとりさま勉強会 #yokodokusho

togetter.com

togetter.com

togetter.com

細々とやっているのですが間が空きました。また続けていこうと思います。
なんか難しくなってきて全く進まなくなってきましたんですが
ここで絶望して投げ出す癖を絶対に治すぞ〜〜〜

昨日のJava女子部で
身の回りの優秀な方は皆本を読んでいるよねという話になって
そうだよねって改めて思ったからです・・・

全然勉強しないから成長が遅いという当たり前の因果が
いよいよ辛くなってきたので、もう少し気合入れて頑張ります😓

Now or never! Nothing is too late to start!

そうそう、サイ本読んでると、当たり前のことなのに説明回りくどくない?って時々思う。
でも例えば経験のない人とか、他の言語やってきた人とかが理解するには
こういう表現が必要なのかもしれないなーと納得しています。

どんな読み手を想定するか?どこまで噛み砕くか?何を知ってる前提で話すか?
本を書くことの難しさを感じるぜ(アホっぽい)。

Scalaのforeachはパターンマッチで書けるのね

val hoge = List(1, 3, 5, 7)

こんなリストがあるとして

順番に要素を出力してみよう。

👻例1

foreachを使うと

hoge.foreach(h => {
  println(h)
})

こうなる。

👻例2

これをパターンマッチと再帰的なサムシンでも書けるっぽい。

@tailrec
def f(hoge: List[Int]): List[Int] = {
  hoge match {
    case h :: fuga =>
      println(h)
      f(fuga)
    case _ =>
      List.empty[Int]
  }
}

f(hoge)を呼び出せば例1と同じになる。

何故急にこんなことを書いてみたかというと、
例2にあるcase h :: fuga => の意味が分かった記念。
最初::が要素追加かと思って何をしているのか理解できんかったけど、リストを順番に処理しているのね。

ちなみにここでの再帰処理を図におこすとこんな感じだと思う!

Scalaのパターンマッチはとにかく強いらしい…?

Scala入門者向けハンズオンに参加した #ScalaNyumon

1週間前ですが・・・
6月5日(土)はScala入門者向けハンズオン@東京に行くことが出来たー!

講師は@gakuzzzzさん!
資料は@mike_neckさんの!
github.com

豪華٩(๑❛ᴗ❛๑)۶

いっぱい知見を得たのでまとめた!(何故かうまくembedできない…)
http://togetter.com/li/986758/togetter.com

最初にScalaについてご説明があって、そのあとは簡単なチケット管理システムを作るハンズオン。


ということでブログにはハンズオンのことをさらっと書いておきたい。

こんな流れ↓で進めてくださり、最後、チケットの検索や更新をするTicketRepoに自力でメソッドを追加してみよう〜というものだった!
カリキュラム · mike-neck/scala-study Wiki · GitHub
メソッドの仕様もここにあります。それにしても資料、丁寧・・・

私もTicketRepoの中身を書いてみたものの怪しい実装だらけになったので、
みけさんの実装と比べてみようと思う。

※私のはチケットをつっこむMapの名前がticketsで、みけさんのはmapなので以下のコードたちもそこの違いはあり〼

findAll: Seq[Ticket]

  • 🐳こう書いてみた。
def findAll: Seq[Ticket] = {
  tickets.values.toSeq
}
  • 🐱IDでソートしてあった・・・親切・・・
def findAll(): Seq[Ticket] = {
  map.values.toSeq.sortBy(_.id)
}

findById(id: TicketId): Option[Ticket]

  • 🐳🐱同じだった。
def findById(id: TicketId): Option[Ticket] = {
  tickets.get(id)
}

createIssue(title: String): Issue, createBug(title: String): Bug

createIssueは割愛

  • 🐳自信なさげにこう書いた。currentIdという怪しい変数、最悪っぽい…。
def createIssue(title: String): Issue = {
  val newIssue = new Issue(currentId, title)
  tickets = tickets + (currentId -> newIssue)
  currentId += 1
  newIssue
}
  • 🐱こうやればcurrentIdいらないね。あとIssuecase classなのでnewしなくてよかった、あちゃ。
def createIssue(title: String): Issue = {
  val nextId: TicketId = if (map.isEmpty) 0 else map.keys.max + 1
  val issue: Issue = Issue(nextId, title)
  map = map  + (nextId -> issue)
  issue
}

findIssuesByStatus(staus: String): Option[Seq[Issue]], findBugsByStatus(status: String): Option[Seq[Bug]]

findBugsByStatusは割愛

  • 🐳空のSeqを返す術しか分からなくて最後に苦し紛れの一文を入れた('_')ちーん
def findIssuesByStatus(status: String): Option[Seq[Issue]] = {
  val issues = tickets.collect {
    case (id, ticket: Issue) if TicketStatus.isMatchedStatus(status, ticket) => ticket
  }.toSeq
  if (issues.nonEmpty) Some(issues) else None
}

ちなみにTicketStatus.scalaにもメソッドを追加してある。

def isMatchedStatus(status: String, ticket: Ticket) = {
  status match {
    case "open" => ticket.status == TicketStatus.Open
    case "fixed" => ticket.status == TicketStatus.Fixed
    case _ => false
  }
}
  • 🐱ぎょーなんかシンプルだ。
def findIssuesByStatus(status: String): Option[Seq[Issue]] = {
  val st: Option[TicketStatus] = TicketStatus.of(status)
  st.map(s => {
    map.values.toSeq collect {
      case x: Issue if x.status == s => x
    }
  })
}

TicketStatus.scalaのメソッド〜。そもそもここの戻り値がOptionなのねそうなのね。

def of(status: String): Option[TicketStatus] = {
  status.toLowerCase match {
    case "open" => Option(Open)
    case "fixed" => Option(Fixed)
    case _ => None
  }
}

fix(id: TicketId): Boolean

  • 🐳ひどいよ〜(;▽;)else return falseなんて書きたくないよ〜(;▽;)
def fix(id: TicketId): Boolean = {
  findById(id) match {
    case Some(t: Issue) =>
      if (t.status == TicketStatus.Open)
        tickets = tickets.updated(id, new Issue(t.id, t.title, TicketStatus.Fixed))
      else return false
    case Some(t: Bug) =>
      if (t.status == TicketStatus.Open)
        tickets = tickets.updated(id, new Bug(t.id, t.title, t.description, TicketStatus.Fixed))
      else return false
    case _ => return false
  }
  true
}
  • 🐱あ。あ。existsでチェックすればいいのだ。
def fix(id: TicketId): Boolean = {
  val ticket: Option[Ticket] = map.values.find(t => t.id == id && t.status == Open)
  ticket.exists(t => {
    map = map.updated(t.id, t match {
      case x: Issue => Issue(x.id, x.title, Fixed)
      case x: Bug => Bug(x.id, x.title, x.description, Fixed)
    })
    true
  })
}

修行あるのみ✋(--)👌


最後にしょーもない感想。
今まで、既に動いてるシステムの改修しかしたことなかったので
trait作って、objectenum作って、case class作って・・・って
新規作成の流れを体験できたのはとても勉強になったー。

あとはpackage objectとかsealedとか知らなかったりよく分かってなかったりしたことも理解できてよかったなー。

学び多きイベントをありがとうございました!

*1:forkしました

Dockerで予め欲しいデータをつっこんだMySQLをこしらえる(Mac用)

MySQLいっぱい生み出して使い分けがしたかったので調べた。
今はまだコンテナが死ぬとデータも死ぬ仕組みなので、主にDaoあたりのユニットテストで一時的なデータ入れるためのDBとして使っておる。

うまくいくまで悩んだりもしたので、やり方だけメモしておく!
ちなみにMac OS X El CapitanVirtualBox使って動かしているよ!!

やってみること

  1. MySQLをDocker上で動かす
  2. hogeデータベースを作る
  3. hoge.usersテーブルを作る
  4. dukeユーザを作り、hogeに実行権限を付与する

インストール

VirtualBox, Docker Machine, Docker Toolboxをインストール

$ brew cask install virtualbox
$ brew install docker-machine
$ brew cask install dockertoolbox

DockerfileとSQLの用意

サンプル

こんな感じ。

#Dockerfile

# FROM コマンドでDocker公式のMySQLイメージを使える(コロン以降がバージョン)
FROM mysql:5.7

# RUN コマンドでDocker側で実行するシェルのコマンドを書ける
# MySQLの設定をこんな感じで追加しておく
RUN { \
    echo '[mysqld]'; \
    echo 'character-set-server = utf8'; \
} > /etc/mysql/conf.d/charset.cnf

# COPY コマンドで自分のマシンからDockerの/docker-entrypoint-initdb.d/下にprepare.sqlをコピーする
# /docker-entrypoint-initdb.d/ 下にあるSQLは全部勝手に実行してくれる!!!
COPY prepare.sql /docker-entrypoint-initdb.d/prepare.sql
--prepare.sql

-- これはいらないかも...
DROP DATABASE IF EXISTS hoge;
DROP DATABASE IF EXISTS fuga;
-- データベースを作る
CREATE DATABASE hoge;

-- dukeユーザをつくってよしなに権限をつける
GRANT ALL PRIVILEGES ON hoge.* TO 'duke'@'localhost';

-- テーブルを作る (実際はちゃんとしたDDLを用意する)
CREATE TABLE `hoge`.`users` ~ ;

要は、実行したいSQL(登録したいデータ)入りファイル/docker-entrypoint-initdb.d/にコピーすればよい!
複数ファイルあっても、コピーした分全部実行される。あと、ダンプファイルとかそのまま使えるのでべんり!

応用編(正しいかは分からないけどこれで解決できた)

  • SQLが複数ファイルにまたがっているが、実行順を制御したいとき(外部キーがあるとか...)
    /docker-entrypoint-initdb.d/にコピー後のファイル名を、lsコマンドで上から実行順に並ぶよう工夫する。
    つまり、a.1番目のファイル.sqlb.2番目のファイル.sql、...みたいにすればその順番に実行できるよ〜。強引。
  • 操作対象のデータベースを変更したいとき(データベース指定のないSQL文を実行するとか...)
    USE hoge とか書いたファイルを作って、意図した通りの順序で実行する。

初回セットアップでやること

testという名前でDockerホストを作成

IPアドレスの指定が不要な場合は、--virtualbox-hostonly-cidr "192.168.99.99/24"の部分は書かなくてもよい。自動で採番されるので大丈夫。
指定したい場合は例みたいにして、CIDR形式で書こう!

$ docker-machine create --driver virtualbox --virtualbox-hostonly-cidr "192.168.99.99/24" test

testホストを起動

restartコマンドやstopコマンドもある。

$ docker-machine start test

環境変数を適用

$ eval "$(docker-machine env test)"

Dockerイメージをビルド

latestはイメージのバージョンに該当する。

$ docker build -t test:latest .

起動の度にやること

testホストを起動

$ docker-machine start test

環境変数を適用

$ eval "$(docker-machine env test)"

起動中のコンテナをすべて停止、削除

関係ないものも停止しちゃうので注意。当然だけどまだ何も起動してないならやらなくてもよい。

$ docker stop $(docker ps -aq)
$ docker rm $(docker ps -aq)

test-mysqlという名前でコンテナを立ち上げ (ポート番号はお好きに変更してね)

次の例だと、ローカルからポート3307のmysqlに繋ごうとすればよい。Dockerがポートフォワードしてくれて3306に繋げ変えてくれるらしい・・・

$ docker run --name test-mysql -p 3307:3306 -v ~/docker/mysql/conf.d:/etc/mysql/conf.d -e MYSQL_USER=duke -e MYSQL_PASSWORD=password -e MYSQL_ROOT_PASSWORD=password -e MYSQL_DATABASE=test -d test:latest

はい出来た!これでOK!!!
prepare.sqlに書いたSQLたちが実行された状態でMySQLが誕生するよ〜。

接続できるか確認してみよう!

$ mysql -h$(docker-machine ip test) -P3307 -uduke -p

ちなみに立ち上げで使うコマンドはシェルスクリプトか何かにしてしまえばもっとラクラクになる。

#!/bin/sh

# ホストを起動する
docker-machine start test

# 環境変数を適用する
eval "$(docker-machine env test)"

# 起動中のコンテナをすべて停止、削除する(関係ないものも停止するので注意)
docker stop $(docker ps -aq)
docker rm $(docker ps -aq)

# コンテナを立ち上げる
docker run --name test-mysql -p 3307:3306 -v ~/docker/mysql/conf.d:/etc/mysql/conf.d -e MYSQL_USER=duke -e MYSQL_PASSWORD=password -e MYSQL_ROOT_PASSWORD=password -e MYSQL_DATABASE=test -d test:latest

その他よく使うコマンド(の一部)

実行中のコンテナを確認

$ docker ps

ログ確認 (立ち上がらない時とかはこれをチェック!)

$ docker logs --follow test-mysql

以上!Docker入門出来て嬉しいな!
強引なやり方を採用しているところは、もっとよい術があれば是非教えて下さい!

参考ページ:

JJUG CCC 2016 Springで若者として煽り芸にトライした #jjug_ccc

もう昔のことですが、5/21(土)はJJUG CCC 2016 Springでした。
お越しくださった方、登壇者の皆様、ボランティアスタッフおよび幹事の皆さん、ありがとうございましたー!!

当日の資料やブログ一覧はGitHubにあります。
github.com

私はというと例によって運営したり、ちょこっと登壇したりしていました。

運営のはなし

今回は20分枠作ったり、CfPの選考に皆さんのご意見を取り入れたり、
休憩を長くしたり、コーヒーをお出ししたり、、、
今までと違うことがたくさんありました。
(詳しくはまーやさんのブログ…とか言うとまた手抜きって怒られるかなw)

良かった点、至らなかった点、いずれもあるかと思いますが、
毎回「前回よりよいJJUG CCCだった」と言えるよう改善しながらやっていきたいものです。
感想や気付きを伝えて(or ツイートして)くださったり、アンケートにご協力くださったりする皆々様、本当にありがとうございます!

自分のことを振り返ると、前回までより準備に関わることが出来たのでよかったです。
基調講演で会長がおっしゃってた通り(資料はコチラ)ちゃんと労働力として機能してたはずだし、
失敗を恐れて今までは手を挙げず見守ってた仕事たちにもトライ出来たし、
少しだけ自信やら責任感やら当事者意識やらがUPした気がしています。
手際も容量も悪いし、見事なタイミングで仕事の方も超絶忙しくなったし想像以上に大変でしたが…
何であれ出来ることが増えるというのはいいものです。

登壇のはなし

Java女子部のハンズオン (#ccc_m3)

これはJava女子部メンバーから希望者を募り発足したプロジェクトです。
私はほぼ何もしていなくて、当日チューターとしてお手伝いさせていただいたのみ。

トラブルがつきもののハンズオンですが、
この日は見事、ご参加者全員がチャットツールを動かすところまで行けました〜!
プロジェクトメンバーは毎日本当に準備を頑張ってくださっていたので、
上手くいったのをみて勝手にすごく嬉しかったです(仕事しろ)。

ちなみにプロジェクトはまだ続いてて、7月にJava女子部のイベントとして
彼女たちに勉強会をやってもらう予定なのでよろしくお願いしますー!!

若者枠セッション (#ccc_ab7)

若者にJavaの好きなところ嫌いなところきいてみたというタイトルで、
@Shin_TNMT氏司会のもと、@bitter_foxさん、@iwata_nさん、@aya_122ちゃんと
好きなところ・嫌いなところをプレゼンし、会場からご意見をいただくスタイルでわちゃわちゃやりました。

私は「おっさんを煽る!!!」と意気込んでJavaの嫌いなところ
…というか、困っちゃうところを語らせていただき、
時間いっぱい使ってアドバイスいっぱいいただいちゃいました('_')
なお、資料だけだと色々誤解を招きそうなので公開は控えます。

ちなみに、おっさん*1たちからのアドバイスは皆とてもポジティブなもので、
結構まじでわくわくしました。
大変なことや辛いことにも向き合っていくしかない、将来きっともっとよくなる!よくしよう!
…そう思って頑張っていきたいと思ったよこなでした(雑)。

その他

あと書いておきたいこと…今回は、スペシャルで素敵な出会いがありました!@Java中の方!!

スタッフTシャツ来てうろちょろしていたら「Java女子部スタッフ?」って声かけていただき、
あれよあれよとJava Day Tokyoでインタビューを受けるチャンスをいただくことが出来たのです!
(それに関しては未来のわたしが別途ブログを書くことでしょう、きっと)。

すごく嬉しかったし、自分のような小娘に話しかけてくださるなんてありがたすぎる話ですねぇ。


CCCは毎回新しい挑戦や気付き、人との出会いがあってやっぱり大好きなイベントです。
もちろん運営をやっているとゆったりセッションを聞けない時間帯もありますし、
京都に住み始めて困難も増えてしまいましたが、それ以上に得るものが大きいぞと本気で感じます。

そうそう、去年のCCC後もこんなツイートをしてたんですが、
こういう感覚は今でも続いています。嬉しいことですねぇ。多分。
成長していないということか?違うよな…?むむむ。

なんかブログが久々すぎて、書き方やまとめかたを忘れました。日記になっちゃったよ。おしまい。

*1:あえての表現なので怒らないでください、お兄様♡