ham-capのブログ

プログラミング学習の記録

【Ruby】eachの返り値について

eachメソッドの返り値?

Ruby Silverの勉強をしていて、以下のようなコードの実行結果についての設問があった。

(1..10).each.reverse_each.each do |i|
  puts i
end

実行結果は以下。

10
9
8
7
6
5
4
3
2
1

単純にeachで取り出したものをreverse_eachで逆順にして、それをeachで繰り返し処理するというだけなのだけれど、自分は最初のeachの段階でレシーバである範囲オブジェクトから1が取り出されていると考えてしまって混乱してしまった。

ポイントは、eachそのものの返り値は何かということを誤解していたことだと思う。

eachの返り値はレシーバ自体

結論から言うと、そういうことらしい。 試しにirbで以下のコードを実行してみる。

(1..10).each do |i|
end
=> 1..10  # <= レシーバが返ってきている

レシーバが配列の場合でも、

array = [1,3,5]
array.each do |n|
end
=> [1, 3, 5] # <= やっぱりレシーバが返ってくる

こうなる。

もう一度最初のコードを見てみると、初めに呼び出されたeachはブロックを渡されない状態で、先ほどirbで実行したのと同じように何の処理もせずにレシーバを返して次のreverse_eachに処理が移っていると考えられる。

reverse_eachもブロックついてないのになぜ逆順になるのか?

最初のコードを見てみると、reverse_eachにもブロックは渡されていないため、先ほどのeachの例に当てはめるとレシーバがそのまま順序も変わらずに次のeachに渡されそうなものだが、実際には2個目のeachによって取り出される値はしっかり逆順になっていた。

これについてはリファレンスマニュアルで言及があり、曰く

ブロックを省略した場合は、各要素を逆順に辿る Enumerator を返します。

とあるので、挙動としてはこれで正しいようだ。

これ、内部的にはreverseが呼ばれた後にeachが呼ばれてるってだけなんじゃないかと思わなくもない。ただの想像だけれど。

今回は以上です。

こたつでもそこそこ快適な作業環境を作るために買ったもの

この記事はフィヨルドブートキャンプ Part 1 Advent Calendar 2023の17日目の記事です。

Part 2もあります。

昨日はayu-0505さんの「初学者が続けているテック系Podcast紹介!」でした!

はじめに

フィヨルドブートキャンプでプログラミングの学習を始めた時点では、プログラミングのプの字も知らない全くの未経験でMacBookAirを買うところから始めました。

当然、家の中の作業環境は整っておらず、最初期の頃はコタツの上にMacを置いて座布団で正座しながらコードを書いていたので、首・肩・背中・腰のHPがゴリゴリ削られていました。

そんな状況から少しずつ便利グッズを買い足していき、今ではある程度快適に学習ができていますが、ぶっちゃけもっと早くにこの状態にすべきだったと感じています。

というわけで、今回はプログラミングの学習をするならこれだけは揃えておいたほうが体のいろんなところを壊さずに済むよというものを紹介したいと思います。

初期装備

まずは初期装備について。

  • こたつ
  • 座布団
  • MacBookAir(13インチ)

以上です。

ヒドすぎますね。どう考えてもエンジニアを目指している人間の装備ではありません。

現在の装備

現在の装備はこんな感じ。ちなみに購入順です。

こたつは変わらんのかい!というツッコミはごもっともですが、エンジニアとして転職できてもできなくても、今の家からは引っ越そうと思っているためデスクの購入は見送っています。

本当は写真を載せた方が伝わりやすいんですが、なにぶん倉庫として使っている部屋の片隅にひっそりと存在する学習コーナーです。散らかり方が地獄すぎますのでお察しください。

とにかく、少なくともプログラミングの学習には必要十分な装備ですので一つずつご紹介します。

各装備の紹介

ゲーミング座椅子

まずはこれ。

そもそも座布団でPC作業をするというのが無謀なのは想像に難くないと思います。

それでも最初の数ヶ月は座布団単騎でなんとかしていたんですよ。自分が怖い。

こたつを活用したかったため、一般的なコロコロがついているタイプの椅子ではなく座椅子タイプとしました。

重要なのは椅子そのものの高さではなく、肘掛けと頭まで支えてくれる背もたれです。

プログラミングに限らずPCで作業をするならこの二点は必須と言っても過言ではないでしょう。

今の職場に言ってやりたい。

item.rakuten.co.jp

中華製タブレットタブレットスタンド

以前は技術書も電子版ばかり買っていました。

Macの画面上でKindleアプリを立ち上げてエディタと並べてもいいのですが、やはりちょっと狭いということでタブレットとスタンドを同時に購入しました。

これが地味に便利で、書見台に技術書を置くよりも多少コンパクトですし、簡易デュアルディスプレイ風の使い方もできます。

ただし、最近は電子書籍から紙の本へ回帰しつつあるので若干持て余し気味。

https://www.amazon.co.jp/gp/product/B06XG7XJ8K/ref=ppx_yo_dt_b_asin_title_o09_s00?ie=UTF8&th=1

タブレットのリンクはAmazonになかったので割愛

トラックボールマウス

Macトラックパッドだと範囲選択がしにくかったり、右クリック(二本指でトラックパッド押し込み)で誤操作が頻発してイラッとするのを解消したくて購入。

普通のマウスじゃなくてトラックボールにした理由はマウスをグリグリ動かすスペースがなかったから。

しかし、使ってみると今までなぜマウスなんてものを使っていたのかわからなくなるぐらい快適で、手首の痛みから解放されました。

欠点はカーソルのエイムが若干おぼつかなくなること。

そのためFPSプレイヤーには向きませんが、単純にPCで作業だけするような人なら断然こちらをオススメします。

欲を言えばもう少しボタンが多い方がいいですが、それだと上位モデルになってしまってそこそこのお値段なのでこちらにしました。

買い換える時は上位モデル一択。

https://www.amazon.co.jp/gp/product/B08L4ZR79Q/ref=ppx_yo_dt_b_asin_title_o09_s00?ie=UTF8&th=1

※リンクはエントリーモデルです。

左右分割キーボード

憧れの自作キーボードで左右分割式のものを購入しました。

と言ってもハンダづけ不要の商品だったので精密ドライバーで組み立てただけです。

記憶が朧げですが、キーキャップとキースイッチも合わせると2万6千円ぐらいしたような気がするので安くはないですが、ゲーミング座椅子と合わせるといきなりコードを書くのが楽になりました。

ポイントは自分にとって自然な高さにゲーミング座椅子の肘掛けの高さを調節し、その延長線上にキーボードを配置すること。

そうすることで、ふんぞり返った姿勢のままタイピングができてしまいます。

これにVimを組み合わせればもう何も怖くありません。

我が家のこたつは1人用で高さも低く、そのままだと肘掛けの高さまでキーボードが来てくれないため、やむなく『進撃の巨人』のコミックスを左右三巻ずつキーボードの下に積んでスペーサーのようにしています。

どうしても作業が進まない時は最終巻を読んで心を落ち着けることもできます。

7sProshop.yushakobo.jp

PCスタンド

一番最近買ったもの。

しかしもっと早く、キーボードと同じタイミングで導入すべきだったと後悔しています。

なぜかというと、外部のキーボードを使用するとどうしてもその分Mac本体が遠くなってしまい、13インチの画面だと文字が小さくなるから。

その上ゲーミング座椅子で背もたれに体を預けているため余計に遠くなり、結果として目がめっちゃ疲れます。

キーボードブリッジを買って本体のキーボード直上に外部キーボードを置くという選択肢もありましたが、左右分割であることの旨味がスポイルされるため、こちらのPCスタンドに落ち着きました。

https://www.amazon.co.jp/gp/product/B07H774Q42/ref=ppx_yo_dt_b_asin_title_o09_s00?ie=UTF8&psc=1

まとめ

今回紹介したものの中でも特にゲーミング座椅子、左右分割キーボードおよびPCスタンドは令和の三種の神器と呼ばれているという説もあります。

冗談抜きで、この三つは「タイピングでものを書く」という行為の快適さを劇的に向上させてくれるので、エンジニアだけでなくライターの方や大学院生の方など、文章をよく書かれる全ての方にオススメしたいです。

まぁ、昇降式のデスクがいいとか大型のディスプレイ2枚あるのが最強みたいなのもあると思いますが、プログラミング始めたばっかりで続くかどうかもわからないという人が買うにはハードルが高いですよね。

なので今回は自分が最低限の快適さを確保するために買ったものを紹介してみました。

誰かの参考になれば嬉しいです。

最後までお読みいただきありがとうございました!

【Ruby】ぼっち演算子でNoMethodErrorを回避する

ぼっち演算子とは何か

Ruby2.3から実装された機能で、るりまの説明によると

safe navigation operator (ぼっち演算子): object&.foo という形式のメソッド呼び出し形式が追加されました。これは object が nil でないときにメソッド foo を呼び出します。 Active Support の try! と似ていますが、メソッド名は文法的に必須であるという点が異なります。

出典:https://docs.ruby-lang.org/ja/latest/doc/news=2f2_3_0.html

とのこと。

「objectがnilでないときにメソッドfooを呼び出します」ということは、レシーバがnilの場合のみ、続くメソッドを呼び出さずにそのままnilを返すということになります。

これを使うと何がうれしいかというと、メソッド呼び出し時のレシーバがnilの場合でもNoMethodErrorを発生させずに処理を続けられる、という点です。

使い方は通常のメソッド呼び出しで使用する.と同じです。

使いどころ

例えば、ある変数をレシーバとしてメソッドチェーンで処理を実行する際、その変数の中身が何らかのクラスから生成されたインスタンスの場合とnilの場合どちらもあり得るという状況だとします。

そういった状況で且つ、nilの場合であってもNoMethodErrorにしたくない(nilでも構わない)というような場合にぼっち演算子が便利です。

例えば、DBから取得したユーザーを変数@userに格納し、.idのようなアクセサメソッドでユーザーIDを返して別のメソッドの引数として渡すような処理をしたいとします。 もし、この引数に渡るのがユーザーIDであれnilであれ、どちらでも問題ないということであれば、存在しないユーザーを取得しようとして@usernilが入ったときに.idNoMethodErrorで止まってしまうと困るわけです。

かと言って、わざわざ条件分岐をしたりするのも無駄に複雑になるだけです。

そんな時に、以下のように&.を使うことで、レシーバがnilの場合には続くメソッドを無視してNoMethodErrorを回避できます。

@user&.id

ただし、いくら&.を使ったところで、レシーバがnil以外の場合にメソッド名をタイポしていたりすると当然NoMethodErrorとなりますので注意が必要です。

まとめ

ちょっとした機能ではありますが、これがあるだけで条件分岐を減らすことができる場面がけっこうあると思います。

そういう場面でスッと思い出せるように覚えておきたいものです。

今回は以上になります。

【React】propsで受け取ったデータを自身のstateにコピーしてはならない

先日、フィヨルドブートキャンプのプラクティスにReactが追加されました。 自分のタイミング的にもちょうどよかったので、早速入門してハマり倒しています。

というわけで、今回はReactにおけるpropsの扱い方に関して気づいたことを書きます。

結論としてはタイトル通りですが、詳しく見ていきます。

propsって何?

詳しくは公式ドキュメントを見ていただくとして、簡単に言うとコンポーネントが外部からの入力を受け取るときに使用する専用の箱のようなものです。

また、propsはオブジェクトです。

値を渡すときはこんな感じ。

<MyComponent foo="hoge" />

MyComponentというコンポーネントに、キーがfoo、バリューがhogeのセットを渡しています。

つまり、MyComponentのpropsには{foo: "hoge"}が渡っています。

したがって、MyComponent内でこれを参照する際は以下のようになります。

console.log(this.props.foo);
// => hoge

stateって何?

stateはコンポーネントが持つ状態のことです。

ユーザーからの入力に応じて変わるコンポーネントの状態を管理するために使います。

詳しくはまたしても公式ドキュメントをご参照いただきたいのですが、例えばユーザーが入力フォームに文字を入力するたびに、そのフォームに紐づいたstateをリアルタイムに更新する、といったことが実現できます。

コンポーネントのstateを子コンポーネントのpropsに渡すことができる

Reactではコンポーネントの中で別のコンポーネントを呼び出すことができます。

仮にコンポーネントAがコンポーネントBを呼び出しているとき、Aが親でBが子という関係になるのですが、このときAのstateをBのpropsに渡し、Bの内部で参照することができます。

では、Aのstateがユーザーの入力によって更新された場合どうなるでしょう。

Reactでは、Bが受け取ったpropsもきちんと更新されます。とても便利。

ただし、これには落とし穴もあります。

propsで受け取ったデータを自身のstateにコピーしてはいけない

ここからが本題です。

公式ドキュメントのこのページに以下のように書いてあります。

props を state にコピーしないでください。これはよくある間違いです。

constructor(props) {
 super(props);
 // してはいけません
 this.state = { color: props.color };
}

この問題はそれが不要(代わりに this.props.color を直接使用することができるため)であり、バグの作成につながる(color プロパティの更新は state に反映されないため)ことです。

つまり、親から受け取ったpropsを子が自分自身のstateとして保持すると不具合の原因になるということです。 前項に倣って親をA、子をBとして説明します。

何がまずいかというと、Aのstateが更新されたことをBが検知できなくなることです。

なぜそうなるかというと、この場合のBのstateはあくまでもコンストラクタが呼ばれた時点のpropsをコピーしたものであって、Aから渡されたpropsをリアルタイムに参照しているわけではなくなるからです。

こうなると、Bのコンストラクタが再度呼ばれてpropsが再度コピーされない限りは、せっかくReactがAのstateの更新をBに伝えてあげても無視してしまうことになります。

このことについて公式では、

意図的にプロパティの更新を無視したい場合にのみ、このパターンを使用してください。

と明言されているため、基本的にこのような書き方をするメリットは無いと思ってよさそうです。

解決策

これを回避するためにはどうすればいいかというと、素直にBの中でもpropsをそのまま使用すればよいだけです。

わざわざコンストラクタでstateの初期値としてコピーせずとも、this.props.fooのように渡されたpropsをそのままコンポーネント内で参照すればOKです。

まとめ

僕はまだReact初心者なのでちょっとしたことでもハマってしまうのですが、今回のテーマについては、Reactの利点を見事に潰してしまうようなポイントであるにもかかわらず、結構やりがちなんじゃないかなと思ったので真っ先に記事にしてみました。

同じようなところでハマっている初心者の方のお役に立てれば幸いです。

【Node.js】Node.jsのコード内からSQLite3を使ってみる

フィヨルドブートキャンプでNode.jsを使ってCLIを作成する課題があり、データの保存先をSQLiteにしたくてnode-sqlite3というnpmを使ってみたので、導入方法をメモしておきます。(SQLite本体がマシンの中に入っていることと、Node.jsがインストールされていることが前提になります。)

インストール

$ npm install sqlite3 --save-dev

ローカルインストールなので--save-devオプションをつけました。

モジュールの読み込み

import * as sqlite3 from "sqlite3"

エラー。 どうして…。

TypeError: sqlite3.Database is not a constructor

上記のエラーを調べてみると、同じ状況のissueを見つけました。

TypeError: sqlite3.Database is not a constructor with ES6 imports

ここの回答によるとどうやらimportの書き方を微妙に間違えているようで、以下のように書き直すことで解決しました。

import sqlite3 from "sqlite3";

任意のクエリを発行

あとはデータベースとテーブルを作成すれば、Node.jsのコード内で任意のクエリを発行してDBの操作ができるようになります。

const db = new sqlite3.Database('DB名');
db.serialize(() => {
  db.run('CREATE TABLE if not exists テーブル名(カラム名)');
  db.run('INSERT INTO テーブル名 (カラム名) values(?)', ['hoge']);
});

注意点

sqlite3が行う処理は基本的に非同期処理となります。 そのため、上記のようにテーブルを作成→レコードの保存という順序を守って同期的に処理を実行したい場合はDatabase#serializeメソッドで一連の処理を囲んであげる必要があります。

まとめ

以上のように、かなり手軽に導入できてすぐにSQLiteを操作することができるようになります。 特にMacは最初からSQLiteが入っているのでnpmのインストールだけで必要な作業はほぼ終わってしまうため非常に便利です。

参考

node-sqlite3

SQLite入門

node.jsでsqlite3を利用する

【Git】プルリクに関係のない変更が紛れ込んでしまった時の対処法

今回はフィヨルドブートキャンプの課題レビューで指摘され、久しぶりにGitであれこれやってみたことを書きます。

やりたいこと

プルリクエストに不必要な変更が紛れ込んでしまったため、修正してdiffを綺麗にしたい。

前提条件

  1. mainからdevelopブランチを切って作業していた
  2. developブランチでFile_AとFile_Bの修正を行いコミットした
  3. そのコミットをリモートブランチにプッシュし、プルリクエストを作成した
  4. File_Bに対する変更は今回のプルリクには不必要なため、diffに含まないようにとの指摘を受けた

手順

調べてみたところ、こういった場合はリモートブランチそのものを正しい状態にすればプルリクにも反映されるとのことだったので、今回はローカルで修正してリモートブランチを上書きするイメージで進めました。(主にこちらの記事を参考にさせていただきました。)

ケースバイケースで適切なやり方は異なるとは思いますが、今回はgit reset --softを使って過去に戻り、File_Bがコミットに含まれないように修正しました。

何はともあれgit resetで過去に戻ります。

オプションは--softにしましたが、--mixedでも良かったかもしれません。

これらに--hardを加えた3つがgit resetの際に使用する主なオプションです。(本当はもっとたくさんあるっぽいです。)

$ git reset --soft HEAD^

つまり、コミットをする前、ファイルに変更を加えてgit addまでは終わっている状態ということになります。 git statusで状態を確認すると、

$ git status
On branch hogehoge
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
    modified:   File_A
    modified:   File_B

こんな感じでFile_AとFile_Bがインデックスに追加されているのがわかります。 今回はコミットからFile_Bを除外したいので、インデックスから取り除きます。

$ git restore --staged File_B

もう一度git statusで確認するとインデックスにはFile_Aのみが登録されている状態になるはずです。

あとは普通にコミットすればローカルでの修正は完了です。

コミット後、リモートブランチにプッシュします。

今回の場合は自分しか触らないリポジトリなので、git push --forceで強制的にプッシュしてしまってもいいのですが、いざ複数人で作業をすることになった際に--forceに対して心理的抵抗が全くないのは問題な気がするので、今回はgit push --force-with-leaseを実行しました。

$ git push --force-with-lease

これでコミットの中の余計な変更をプルリクから排除できました。

--forceは取扱注意

今回、ローカルでの修正をリモートに反映させるためにgit push --force-with-leaseを使用しました。

これは自分のローカルリポジトリの履歴と比較して、リモートリポジトリに既に変更が加わっている(他の誰かが先にコミットしている)状況ではプッシュを強制できないようにするオプションで、無条件に--forceでプッシュするよりは他人の努力を水泡に帰する危険性が減ります。

とはいえ、直前にリポジトリ自体をfetchしてきている場合はプッシュできてしまったりするらしいので、絶対安全なものではありません。

いずれにしても、チームで開発するときはそのチームのルールを遵守して適切に対処すべきと思います。

安易に--forceでプッシュとかは絶対アカンやつ。(まだチームで開発したことないけど…)

まとめ

今回は歴史改変のお話でした。 実は最初にgit reset --softを実行する前に--hardで実行してしまい、不必要に巻き戻しすぎたので、git reflogを使ってそのresetを更に無かったことにしたりしてますが、その辺は今回の本筋からはそれてしまうので別記事にまとめたいと思います。

参考

GitHub プルリクエスト後に間違いに気づいたとき

[git reset (--hard/--soft)]ワーキングツリー、インデックス、HEADを使いこなす方法

git push -f をやめて --force-with-lease を使おう

【Ruby】おとなしそうな顔して破壊的メソッドな奴ら(Array#shift・Array#unshift)

初めて使ったメソッドが思いがけず破壊的メソッドだったよって話。

目次

破壊的メソッドでも末尾に必ず「!」がつくとは限らない

Rubyの各クラスで使用できるインスタンスメソッドには破壊的メソッドと非破壊的メソッドというのがあります。

それぞれ大雑把にいうと、

  • 破壊的メソッド:レシーバとなるオブジェクトの値そのものに対して処理を実行する
  • 非破壊的メソッド:レシーバとなるオブジェクトに何らかの処理を行なった結果を新しいオブジェクトとして返す(レシーバのオブジェクトの値は変わらない)

という感じです。

で、破壊的メソッドの場合は基本的にメソッド名の後ろに!がつきます。 例えばgsubは非破壊的メソッドで、gsub!は破壊的メソッドです。

ただし、これはあくまでも原則であって全ての破壊的メソッドに!がついているわけではありません。 というか、!のついていないものも普通にあります。

その中で今回はArrayクラスのshiftunshiftについて、自分が初めて使った時に非破壊的メソッドだと思って使って騙されたので備忘録的な記事です。

shift

まずはshift。以下はるりまの定義です。

配列の先頭の要素を取り除いてそれを返します。引数を指定した場合はその個数だけ取り除き、それを配列で返します。

空配列の場合、n が指定されていない場合は nil を、指定されている場合は空配列を返します。また、n が自身の要素数より少ない場合はその要素数の配列を返します。どちらの場合も自身は空配列となります。

返す値と副作用の両方を利用して、個数を指定して配列を 2 分する簡単な方法として使えます。

class Array (Ruby 3.0 リファレンスマニュアル)

挙動としてはこんな感じ。

a = [0, 1, 2, 3, 4]
first_element = a.shift

p a
=> [1, 2, 3, 4]   

p first_element
=> 0

shiftのレシーバとなったオブジェクトaの値自体が変更されているため、first_element = a.shiftより後で呼び出すと先頭の0がなくなっています。

unshift

次がunshift

指定された obj を引数の最後から順番に配列の先頭に挿入します。引数を指定しなければ何もしません。

class Array (Ruby 3.0 リファレンスマニュアル)

こちらはshiftの逆で先頭に要素を追加します。

a = [0, 1, 2, 3, 4]
a.unshift(5)

p a
=> [5, 0, 1, 2, 3, 4]

こちらもshiftと同様、aの値自体が変更されているのがわかります。

まとめ

まぁなんてことはない話なんですが、なんとなく無意識で!がついていなければ非破壊的なメソッドと思い込んでしまっていることがあるので、意図しない挙動になってしまい、思わぬところでエラーを出さないとも限りません。

また、今回紹介したshiftunshift!がつかない破壊的メソッドのうちのほんの一部です。

他にも各クラスに結構ありますので、初めて使うメソッドはちゃんと動きを確認してから使うよう心がけたいですね。