Quantcast
Channel: rana_kualuの記事 - Qiita
Viewing all 337 articles
Browse latest View live

開発者としてお金を稼ぐ方法のアイデア

$
0
0

以下はFlorin Popによる記事、Ways to make money as a developerの日本語訳です。

Ways to make money as a developer

開発者としてお金を稼ぐ方法のアイデアを一覧に書き出しました。
私はこのリストをしばらく前に作ったのですが、これに価値を見いだす人がいるかもしれないので、共有することにしました😄

  • 最も一般的なのは、就職するか、フリーランサーとして働くこと。
  • 何らかのデジタルプロダクトを作成し、販売する。プラグイン、コンポーネント(React、Angular、Vue、Bootstrap他)、テーマなどなど。
  • SaaSを構築する。
  • 広告アプリ内購入のできるWebアプリモバイルアプリデスクトップアプリを作る。
  • ブログを作ってマネタイズ。
    • Google Adsense、carbonadsなどの広告。
    • スポンサーを付ける。
    • アフィリエイトで商品を宣伝する。
    • 自社製品を宣伝する。
    • 出版物の記事を書いて対価をもらう。
  • 電子書籍、あるいは物理書籍を書いて販売する。
  • Youtubeにチャンネルを作成する。
    • Youtube広告を貼る。
    • スポンサーを付ける。
    • アフィリエイトする。
    • 自社製品を宣伝する。
  • オンラインコースオンラインプログラムを作成する。
  • TwitchやYoutubeなどでライブストリームを行う。
    • 寄付を募る。
    • 月額サブスクリプションにする。
  • podcastを公開する。
    • スポンサーを付ける。
    • 自社製品を宣伝する。
  • オープンソースプロジェクトにコントリビュートし、寄付スポンサーをゲットする。
  • Patreonにページを作成し、他者があなたをサポートできるようにする。
  • ジョブボードを公開し、開発者やデザイナーと共に企業から仕事を受ける。
  • swagを売る。
  • ニュースレターを売る。
  • 対面でコーチングする。
  • トレーディングBOTを作成する。
  • 報酬の出るコーディングチャレンジに参加する。
  • バグバウンティングプログラムに参加する。

リストに追加できそうなものがあれば気軽にコメントください!😄
よかったらここからニュースレターに登録してください。

コメント欄

「著名なインフルエンサーはだいたいひとつ以上のことを実行してるよね。」
「最近OSS始めて主にバグ報告してるけど、GitHubから報酬もらえない。」「GitHubは報酬くれないよ。プロジェクトからもらう。でもよっぽど貢献しないと難しい。」
「Youtubeはチャンネル登録者が10万人はいないとまともな稼ぎにならない。」「そうかもしれないが、でもいきなり10万人から始まった人はいない。」「100人未満のチャンネルやってるけど、うまく宣伝して何千ドルもゲットしたよ。」
「オンラインコースを作成して売るのがいいと思います。継続して努力する必要がなくて、一度作るだけで稼ぎ続けることができます。このことについてブログを書いたよ。」「同意だが、ただ最初にコースを作るときに多大な努力が必要。」
「トレーディングBOTで稼いでる人なんて本当にいるの?」「この人とか。」
「報酬の出るコーディングチャレンジなんてあるの?」「HackerRankとか。」
「忘れてるぞ。『ゼロデイ脆弱性を探してダークウェブで売る』。もしくは『Hackeroneに提出して正当な対価を得る』。」
「Webサイトをハッキングしてユーザ情報をダークウェブで売ろう。」
「アドバイスどおりにトレーディングBOTを作ったんだけどバグが取れません!このエラーが出続けます!『ギブミーマネー!!!』」「それはトレーディングBOTに共通するバグです。」「これは仕様です。」

感想

あなたは、なぜエンジニアの仕事をしているのですか?

私?
そんなの仕事だからに決まってるだろ(循環参照)
仕事なんて趣味に使う金を稼ぐための手段でしかありません。
金さえあれば仕事なんて即座に投げ捨てるね。

そんなわけで何か楽して稼げる裏技でも載ってないかなあと思って訳し始めたのですが、思っていたより真っ当でリーガルな記事でした。
単に何も考えずにこれをやれば儲かるというものではなく、色々なアイデアを示してはいるものの、最終的には自分に合ったものを自分で選ばないといけません。
その方法もグレーやブラックなものではなく、裏技的なものですらなく、正当なルートしか示していません。
まさにアイデア集ですね。
マネタイズを考えている人は参考にしてみてはいかがでしょう。

なお、個人的にはあまり役に立たない内容でした。

私は個人情報を出していないし今後も出すつもりはないので、YouTubeやPatreonのようなルートは対象外です。
リアルにも紐付けていない(リアル友人はほぼ全員このアカウントを知らない)ので、転職やフリーランスに役立てることも難しいです。
継続的な努力なんてやりたくないのでアフィやOSSコントリビュートはできません。
SaaSやデジタルプロダクトにに手を出すにはアイデアが足りず、バグハンターになったり書籍を作るには能力が足りません。
多少なりとも秀でてそうなものといえばQiitaのContribution数くらいですが、顔出しせずにQiitaにいくら記事を書いても1円にもならないので、全く意味がありません。

もっとこう、寝てるだけでお金が入ってくるような方法はないものですかね。


2019年の残り期間で学習するべきスキル

$
0
0

Gatsby?ああ、MovableTypeの翻案ね(間違い)

以下はMarc Grabanskiによる記事、What Front-End Developer Skills Should You Focus on Learning for the Rest of 2019?の日本語訳です。

What Front-End Developer Skills Should You Focus on Learning for the Rest of 2019?

こんにちは、私はFrontend MastersのCEO、Marcです。
このたびdev.toコミュニティのスポンサーになれることを嬉しく思います!😀

JavaScript and Front-End Engineering

我々は、地球上で最も急激に変化し、進化する、そして最も活発なコミュニティのひとつに居ます。

JavaScriptはES6以降、毎年多くの進化をし続けています。
Node.jsが現れて以来、多くの企業がサーバでもJavaScriptを使い始めました。
フロントエンドWeb開発者は、ツールやビルドツール、フレームワークの進化と共に変化し、進化し続けています。
その最先端の一部はWeb AssemblyとReact Hooksで、我々がやがて何を作り出せるかを見るのが楽しみです。

さて、2019年の残り時間に集中して学習すべきスキルは何でしょうか。
以下では3カテゴリに分けて紹介します。

  1. Begin Coding Now
    これからコーディングを始める人。

  2. Becoming a Professional Front-End Developer
    フロントエンドエンジニアのプロフェッショナルになりたい。

  3. Becoming a Well-Rounded Engineer
    フルスタックエンジニアになりたい。

1. Begin Coding Now

手短に言うと、最初に正のフィードバックループを手に入れることが最も重要です。
何かを変更して、そうしたらすぐにその結果がわかる、ということです。

Get Started: Scratch, HTML/CSS/JS or Python

始めよう。Scratchでも、HTML/CSS/JSでも、Pythonでも。

私は、どの言語やツールからプログラミングを始めるかは問題ではないと考えています。
それがScratchでも、HTMLでも、CSSとJavaScriptでも、Pythonでも、あるいは何らかのフレームワークであっても。
大事なのは、正のフィードバックループを確立し、アイデアを構築し、プログラミングに興味を持つことです。

2. Becoming a Professional Front-End Developer

Mastering the Fundamentals of JavaScript

JavaScriptの基礎をマスターする。

Frontend Mastersは、JavaScriptの基礎とプログラミングパラダイムは、普遍的なものであると信じています。
スコープとクロージャの仕組み、プロトタイプシステム、および型システムは、あなたのキャリアにおいて何度も何度も学習することが必要になるでしょう。

Know Your Paradigm: Functional and Object Oriented Programming

自分のパラダイムを知ろう。関数型?オブジェクト指向?

JavaScriptはマルチパラダイム言語であるため、オブジェクト指向と関数型パラダイムの両方を学習することで、より高次のレベルに到達しやすくなります。
オブジェクト指向は大規模なアプリケーションを構築する一般的な手法です。
その次は関数型からmap、reduce、filter、純粋関数、コンポジションといった概念を取り込みましょう。

最後に、オブジェクト指向と関数型をどのように使い分けるかを身につける必要があります。

React or Vue?

プロフェッショナルなコードを素早く作成したい場合、最も早い方法はReactVueといったフレームワークを使うことです。
大抵の仕事では、これらトップフレームワークの少なくともひとつを深く知っていることを要求されます。

多くの知識人は私がAngularに言及しなかったことに反応するかもしれません。
しかし、Angularは初めて扱うのに適したフレームワークだとは思いません。
なんでもかんでも入ってる、という思想を活かすのに十分な規模のプロジェクトは、最初のうちはやってきません。

Developer Tools

開発者ツールの使い方を習得し、コードをデバッグし、アプリケーションのパフォーマンスを向上させましょう。

TypeScript

開発者のエクスペリエンスを向上させるためにTypeScriptを導入する企業は年々増加しています。

CSS Grid and Flexbox

CSS GridとFlexboxは、あらゆるデバイスで動作するレスポンシブWebサイトをレイアウトするために不可欠です。

Webpack

Create React AppやParcelといったツールは導入を簡単にしてくれますが、より詳しくなるためにはWebpackを学んでください。
出力コードを最適化するためのカスタムビルドを作成しましょう。

3. Becoming More Well-Rounded

Design Skills

基本的なデザインスキルを手に入れることで、より望ましいオールラウンド開発者になることができます。

Node.js & Full-Stack Deployment.

Node.jsを学び、APIを構築する方法を学び、フルスタックエンジニアになりましょう。

アプリを自分でセットアップ・デプロイできるなら、さらに応用範囲が広がります。

現在はアプリのデプロイ先としてAWSが最も勢いのあるプラットフォームですが、Azureも人気を集めつつあります。

SVG

最も能力に秀でていて、そして最も活用されていないグラフィックフォーマットのひとつがSVGです。
モバイルデバイスから看板サイズまで自在に拡縮することができる、非常に優れた形式です。

Testing

壊れたコードを作っていないことを保証したいですか?
Jestのようなテストランナーでテストを行い、壊れたデプロイを防ぎましょう。

Git

コードを失った?
あなたがGitマスターであれば、コードを失うことは決してなく、マージ時に問題が発生したとしても復旧することができます。
Gitのエキスパートは、開発チームの全員から崇拝されるでしょう。

Computer Science

計算量の求め方を計算し、正しいアルゴリズムとデータ構造の使い方を知ることで、エンジニアリング思考力を育て、より効率的なソリューションの開発ができるようになります。

Accessibility

WebサイトとWebアプリケーションがあらゆる人に開かれていることを確認するために、アクセシビリティの理解は重要です。
またナビゲーションにキーボードだけを使用するようなパワーユーザも満足させます。

Newer Skills

高パフォーマンスなWebサイトを作るため、Gatsbyが市民権を得つつあります。

ブラウザで3DのCanvasとWebGLが広くサポートされたため、ブラウザ上でクリエイティブコーディングが可能になりました。

GraphQLはRESTのようにAPIを複数のエンドポイントに分けるのではなく、クライアント側で欲しいデータを正確に指定できるため、APIの柔軟性を高めることができます。

2019年残りの期間に開発者が学ぶべきだと、あなたが考えるスキルは何ですか?

コメント欄

「アマ開発者とプロ開発者の境目はテストだと考えているので、全ての技術スタックでテストを考えるべき」「同意しかない。しばしばテストは後回しにされるけど、TDDは実際最優先されるスキルになりつつある。」
「ここ最近で学習コースを一通り渡り歩いて、FEMは私の人生に必須のツールボックスになったよ。」「それは良かった!あなたのキャリアに幸ありますように!」「あとフロントエンドハンドブックも手放せない。」「ナイス!2020年版も既に作り始めてるよ!」
「現在TypeScriptコースを受講してる。満足したらテストコースやCSコースも受けたい、楽しみ。」
「基礎を全く持ってない人はLinkedInやUdemyのような初心者コースから始めたほうがいい?」「FEMはまさにあなたのためのコースも用意してるよ!ラーニングパスも参考にしてね。」
「Svelteコースがみつからなかった。今後追加される予定はありますか?」「作者のRich Harrisと何かするつもりだけど、まだ決まってないんだ。」
「Accssibility!」「追加した!」
「Flutter…まだモバイルだけだけど、WebバージョンのHummingbirdが近付いてる。Dartはよいぞ。」
「JAMStackとサーバレスにも言及が必要。」

感想

FrontendMastersはフロントエンド中心の学習コースを提供しているサイトです。
費用は月$39、年間$390と決して安くはありませんが、評判は悪くないようで、特に中上級者向けだそうです。
英語のヒアリングが苦にならない人は試してみてはどうでしょうか。

それ以外の活動としては、毎年Front-End Developer Handbookを発表しています。
フロントエンドについてはかなり最強に近いところではないかと思います。

そんなところが勧めるのですから、半分はコースの宣伝だったとしても、学習方針としては適切な方向性を向いているのではないでしょうか。
コースを受講するかどうかは別として、出てきた技術を学習することは決して悪くないと思います。

PHPの繰り返し処理大全

$
0
0

PHP7.3時代の話です。
PHP8や9のころには、また別の結論になっているかもしれません。

最初に結論

・全要素繰り返しはforeach
・途中で打ち切るのはwhile/for
・それ以外はいらん

繰り返し処理一覧

foreach

PHPのforeachは非常に優秀です。
あらゆる反復可能な値を繰り返し処理することができます。

$arr = [1, 2, 3];
foreach($arr as $val){
  echo $val; // 順に1, 2, 3
}

PHPの配列は順序付きリストなので、順番も保証されます。
また、キーと値を両方とも取れるので、inとofどっちだったっけとか悩む必要もありません。

$arr = [3 => 1, 2 => 2, 1 => 3];
foreach ($arr as $key => $val) {
    echo $key, $val; // 31, 22, 13 順番が変わったりはしない
}

オブジェクトにも使えます。

class Test{
    public $a = 1;
    protected $b = 2;
    private $c = 3;

    public function loop(){
        foreach($this as $val){
            echo $val;
        }
    }
}

$test = new Test();
foreach($test as $val){
    echo $val; // 1
}

$test->loop(); // 1,2,3

オブジェクトに使った場合、可視なプロパティが順にアクセスされます。
すなわち外部からはpublicプロパティしか見えず、内部からならprotectedやprivateも見えるということです。

さらに他言語では御法度の、ループ中で自身や別要素を削除するという芸当がPHPでは可能です。

$array = range(0, 10);

foreach ($array as $k => $v) {
    unset($array[$k - 1]);
}

var_dump($array); // [10=>10]

ループ中で、前回ループの値を削除しています。
他言語でこんなことをやるとどんな動作になるかわかったものではありませんが、PHPでは全要素をきちんとループした上で、最終的に$arrayの値は想定通りになります。
配列をforeachした時点で配列のコピーが作成されるので、ループ内で変なことをしでかしてもループ自体には影響が及ばないように対策されているのです。
おかげで何も考えずにfilterが実装できます。

$array = [1, 2, 3, 4, 5, 6];

// 偶数以外削除
foreach ($array as $k => $v) {
    if ($v % 2) {
        unset($array[$k]);
    }
}

var_dump($array); // [2, 4, 6]

while

条件がtrueっぽい値であるかぎり、ループを繰り返します。

$i = 1;
while ($i <= 10) {
    echo $i;
    $i++;
}

foreachとの使い分けですが、foreachは配列などの全要素にアクセスする用途に使います。
whileは何らかの条件でループを脱出する用途に使います。

// 30回ループしたら脱出
$i = 0;
while ($i < 30) {
    echo $i;
    $i++;
}

// 30秒経ったら中断
while(true){
    sleep(1);

    if(time() - $_SERVER['REQUEST_TIME'] > 30){
        break;
    }
}

// 標準入力を出力
while( ($line = fgets(STDIN)) !== false){
    echo $line;
}

うっかり使うと無限ループになるので使用は避けましょう
いや嘘。
whileを使うべきところにforやforeachを使ったりはせず、適切に使い分けましょう。

for

whileで多くの場合必要となる『初期値設定』『カウント処理』を最初から文法に組み込んだものです。

// 30回ループしたら脱出
for ($i = 0; $i < 30; $i++) {
    echo $i;
}

whileではループ前後に書かざるをえなかった定型処理がひとつの文にまとまったことで、わかりやすくなって見た目もすっきりしました。

whileは常にforで書き換え可能なので、実はwhileは必ずしも存在する必要はありません。
しかし初期値設定などが必要ない簡易的なループはwhileで書いた方がわかりやすいので、forとwhileは処理内容によって使い分けるとよいでしょう。

// forを使うほどではない
for(; true; ){
    sleep(1);

    if(time() - $_SERVER['REQUEST_TIME'] > 30){
        break;
    }
}

PHPでは、配列要素などにアクセスする用途でforを使用する理由はありません。

$array = [1, 2, 3];

for ($key = 0; $key < count($array); $key++) {
    echo $key, $array[$key]; // 01, 12, 23
}

$array = [0 => 1, 2 => 3]; // 死
$array = ['answer' => 42]; // 死

歯抜けのない純粋配列にしか使用できず、うっかり連想配列や歯抜けのある配列にforでアクセスすると死にます。
要素に対するアクセスには必ずforeachを使いましょう。

forの存在価値はループのためではなく、条件分岐のためにあります。

for(; time() - $_SERVER['REQUEST_TIME'] <= 30; sleep(1));

条件部分やループカウント部分にあらゆる処理を詰め込むことでforの本文を空にすることもできますが、見づらくなるだけなので止めましょう。

do - while

条件のチェックが最後に行われるということ以外はwhileと同じです。
つまり、たとえ条件が偽でも必ず一回だけは実行されるwhileということです。

$i = 100;
do {
    echo $i++;
} while ($i < 30);

正直、whileではなくこちらを使わなければならないシーンを思いつきません。

関数型関数

PHPには組み込みでarray_walkarray_reducearray_productといった、関数型のように書ける関数が多数用意されています。
これらを使うことで反復処理をやめ、関数型プログラミングっぽい記述にすることが可能になります。

$sum = 0;
foreach($array as $val){
    $sum += $val;
}
echo $sum;

echo array_sum($array); // ↑と同じ

が、基本的にこれらを使う必要はないです。
上記のようなわかりやすい例はむしろ例外で、PHPでは文法の都合上、大抵の処理は関数型で書くと却ってわかりづらくなります。

以下はPHPで高速オシャレな配列操作を求めてより借用した例です。

// 0~10000のうち、偶数だけを抽出して自乗し、結果が20を超えるものを足しあわせよ

// 関数型
echo array_sum(
    array_filter(
        array_map(
            function ($v) {
                return $v ** 2;
            },
            array_filter(range(0, 10000), function ($v) {
                return $v % 2 === 0;
            })
        ),
        function ($v) {
            return $v > 20;
        }
    )
);

// 普通に書く
for ($sum = $v = 0; $v <= 10000; ++$v) {
    if ($v % 2){ continue; }
    $v **= 2;
    if ($v <= 20){ continue; }
    $sum += $v;
}
echo $sum;

明らかに普通に書いた方がわかりやすいですね。
そもそも関数型の例でよく出てくる『○○を抽出する』みたいな処理は、PHPであればSQL発行する時点で絞っておけって話ですし。

filter_varなど使いこなすと色々楽しいこともできるのですが、実用的かと言われると首が傾いてしまいますね。
もちろん関数型で書いてもわかりやすい場合もありますが、別にforeachで書いたところで可読性も大してかわらないので、なら最初から全部foreachで書いた方が手っ取り早いです。

反復処理の定義

オブジェクトに対するループ処理の挙動を、PHPでは任意に定義可能です。
上のほうでオブジェクトをforeachするとpublicプロパティが順番に出てくると言いましたが、それはデフォルトの動作であって、やろうと思えば変更できるということです。

Iteratorインターフェイス

反復処理実装の基本です。
Iteratorインターフェイスをimplementsして各メソッドを実装します。

class Test implements Iterator
{
    public $dummy = '出てこない';
    private $data1 = [1, 2, 3];
    private $data2 = [4, 5, 6];
    private $current = 0;

    /**
     * @Override
     * 現在の値を返す
     * @return mixed 現在の値
     */
    public function current(){
        return $this->data1[$this->current] ?? $this->data2[$this->current - 3] ?? null;
    }

    /**
     * @Override
     * 現在のキーを返す
     * @return string|int 現在のキー
     */
    public function key(){
        return $this->current;
    }

    /**
     * @Override
     * ポインタを次に移動する
     */
    public function next(){
        $this->current++;
    }

    /**
     * @Override
     * ポインタを初期化する
     */
    public function rewind(){
        $this->current = 0;
    }

    /**
     * @Override
     * 現在のポインタが有効か
     * @return boolean 有効ならtrue
     */
    public function valid(){
        return isset($this->data1[$this->current]) ?: isset($this->data2[$this->current - 3]);
    }
}

$test = new Test();
foreach ($test as $key => $val) {
    echo $key, '=>', $val; // [0=>1, 1=>2, 2=>3, 3=>4, 4=>5, 5=>6]
}

publicであるはずの$dummyは出てこなくなり、currentで返した結果が表示されるようになります。
このように、Iteratorインターフェイスを使うことでループで返す値を好き勝手に変更することができるようになります。
ただ、変なことをやってもわかりにくくなるだけなので、基本的にあまり使わないほうがいいと思います。

IteratorAggregateインターフェイス

Iteratorインターフェイスは必ず5個のメソッドを実装する必要があって面倒です。
PHPには最初からイテレータが幾つも用意されており、それらで賄える範囲であれば簡単に実装できるIteratorAggregateがあります。

class Test implements IteratorAggregate
{
    public $dummy = '出てこない';
    private $data1 = [1, 2, 3];
    private $data2 = [4, 5, 6];

    /**
     * @Override
     * イテレータを返す
     * @return Iterator
     */
    public function getIterator(){
        $iter = new AppendIterator();
        $iter->append(new ArrayIterator($this->data1));
        $iter->append(new ArrayIterator($this->data2));
        return $iter;
    }
}

$test = new Test();
foreach ($test as $key => $val) {
    echo $key, '=>', $val; // [0=>1, 1=>2, 2=>3, 0=>4, 1=>5, 2=>6]
}

IteratorAggregate::getIteratorに適当なイテレータを投げれば、foreachループでそれが出てくるようになります。
今回は配列をイテレータにするArrayIterator、複数のイテレータを順にまとめるAppendIteratorを使って、$data1$data2が順に出てくるようにしました。

もちろん、書くのが楽になったからといって使いまくると何が出てくるかわからないブラックボックスになってしまいます。
RecursiveRegexIteratorあたりまで来ると正直何言ってるかわからないので、変なイテレータには手を出さない方がいいと思います。

ジェネレータ

ジェネレータはクロージャとセットにされがちですが、単に何度もreturn(yield)できる関数という認識でいいと思います。

function getPrimeNumber(){
    yield 2;
    yield 3;
    yield 5;
    yield 7;
    yield 11;
    yield 13;
    yield 17;
    return 19; // returnは出ない
}

$primes = getPrimeNumber();
foreach($primes as $prime){
    echo $prime; // 2, 3, 5, 7, 11, 13, 17
}

returnのかわりにyieldというキーワードを使います。
yieldキーワードが入った関数は、関数呼び出しの返り値が自動的にGeneratorインスタンスになります。
たとえ絶対にyieldを通らない実装だったとしてもそうなるので、少し注意が必要です。

返ってくるのはオブジェクトなので、その返り値をforeachでループすることができるわけですが、その際は関数の最初からではなくyieldで止まったところの次の文から処理が再開されます。
関数内の変数値などは維持されるので、何も考えずにメモ化ができたりします。
上で出した例では全く意味がありませんが、無限数列などを作ったりする際にはジェネレータがとても役立ちます。

// フィボナッチ数を求めるジェネレータ
function getFibonacci(){
    $fa = 0;
    yield $fa;
    $fb = 1;
    yield $fb;

    while (true) {
        $fib = $fa + $fb;
        $fa = $fb;
        $fb = $fib;
        yield $fib;
    }
}

$count = 0;
foreach (getFibonacci() as $fibonacci) {
    echo $fibonacci . "\n";
    // 適当に切らないと無限ループする
    if ($count++ > 30) {
        break;
    }
}

再帰もメモ化もgotoもなんもなしに、普通に定義通りのフィボナッチ数を求める関数が書けてしまいました。

ジェネレータは頻繁に出番があるかというと無いですが、覚えておくといざというとき便利な機能です。

まとめ

自発的に使うのはforeachとwhile/forだけでいいよ。

それ以外はあまり出てくるものでもないので、出てきたときに調べるくらいで大丈夫でしょう。

使う必要のないもの

foreachのリファレンス

foreachでリファレンスが取れますが、使用してはいけません。

$array = range(0, 5);
foreach ($array as &$val) {
    $val *= 2;
}
var_dump($array); // [2, 4, 6, 8, 10]

$val = 42;
var_dump($array); // [2, 4, 6, 8, 42] ←

そもそもリファレンスはあらゆる場面で一切使用禁止です。
リファレンスで高速化が云々とか言ってる人は全員間違い1なので、生暖かい目でスルーしましょう。

do - whileの早期return

do-while記法は、簡易的な早期returnに使用できます。
以下はマニュアルに載っている例です。

do {
    if ($i < 5) {
        echo "i は十分大きくはありません。";
        break;
    }
    $i *= $factor;
    if ($i < $minimum_limit) {
        break;
    }
    echo "iはOKです。";

    /* 実際の処理 */

} while (0);

breakはループを抜ける文なので、$i<5だったり$i < $minimum_limitの場合は、このwhileループを抜けます。
条件に当てはまらずループの最後まで来た場合、条件が常に偽であるため、そのままループを終了して先に進みます。
はい、早期returnできました。

もちろんこのような書き方はせず、メソッドなどに出してください。

current / reset / next / prev / end

配列ポインタを手動で操作することが可能です。

$array = range(0, 5);

while(true){
    echo key($array), current($array);
    if(next($array) === false){
        break;
    }
}

配列ポインタ操作関数はresetnextprevendなどひととおり揃っています。
が、あえてこれらの関数を使わなければならない場面はありません。

さらにマニュアルには書かれていないのですが、実はこいつらの引数はZ_PARAM_ARRAY_OR_OBJECT_HTであり、つまりオブジェクトを受け付けます。

class Test{
    public $a = 1;
    protected $b = 2;
    private $c = 3;
}
$array = new Test();

while(true){
    var_dump(key($array), current($array) );
    if(next($array) === false){
        break;
    }
}

01.png

マジかよ。

each

PHP7.2でDeprecatedになったため、使ってはいけません。

いにしえのPHPではメモリ節約のためにeachを使おうなどとされていた時代もありましたが、PHP7時代においては間違った記述です。

上のほうでforeachしたら配列のコピーが作成され云々とか言いましたが実は嘘で、現在ではforeachループするだけならコピーは作成されません。
ループ中で元の配列を変更しようとしたときに初めてコピーが作成されます。
これはコピーオンライトと呼ばれる技術で、最近のPHPの最適化技術のひとつです。
現在のPHPは非常に最適化が進んでいるので、下手なことを考えるより標準機能を使った方がよっぽど使用メモリも少なく速度も速いです。

ジェネレータの変な使い方

素のジェネレータはforeachしかできず、whileやforで書くことができないのですが、実はGeneratorクラスはIteratorインターフェイスをimplementsしているので、手動でループさせることもできます。

$fibonacci = getFibonacci();

while ($fibonacci->key() < 28) {
    $fibonacci->next();
    echo $fibonacci->current(); // 1, 1, 2, 3, 5, 8, …
}

こんな処理が必要になるのであれば、ジェネレータではなく他の機能を使った方がよいでしょう。

yield fromキーワードを使って、別のジェネレータの返り値を取り込むことができます。

function gen()
{
    yield 1;
    yield from [2, 3];
    yield from gen2();
}

function gen2()
{
    yield 4;
    yield 5;
}

$gen = gen();
foreach ($gen as $v) {
    echo $v; // 1, 2, 3, 4, 5
}

こんな処理を作り込むよりも、データ構造を見直した方がよいでしょう。

ソート

usortなんかは関数型っぽい書き方なのでループと言えないこともない可能性がなきにしもあらずな気がしないでもないんだけどループの範疇に含めるべきものだろうか?

$arr = [3, 1, 2];

usort($arr, function ($a, $b) {
    return $a <=> $b;
});

var_dump($arr); // 1, 2, 3

やはりループっぽくはないですね。

その他

思いついたのを並べたらこんなかんじでした。
見落としや間違いがあったら誰かがプルリクしてくれるはず。


  1. 0.1%くらい正しいことを言っている可能性もあるが、見分けは付かないので全て間違いと考えてかまわない。 

便利なオンラインAPIリクエストビルダー、Postwomanの紹介

$
0
0

ついにAPI界にもジェンダーの波が!

というわけでも特になく、単にPostmanフォロアーだからだと思われます。

Postwomanってなに?

Liyas Thomasによって開発された、APIリクエストビルダーです。
平たく言うとオープンソース簡易版Postmanです。

Postmanってなに?

Postmanとは、Qiitaにも幾つか記事がありますが、開発中のAPIを叩いてテストしたりするのに便利な開発ツールです。
ただソースがクローズドだったり一部有料だったり、あとはなんといってもインストール形式なので導入が面倒です。
昔はChromeエクステンションもあったらしいのですが、現在はもう見つかりません。

本格的に重厚長大なアプリを作る際のテストには適しているかもしれませんが、ちょっと数個のAPIを試したいだけなんだ、なんてときにはもっと気軽に使いたいですね。
そんなときはPostwomanの出番です。

Postwomanを使ってみる

Webサイトにアクセス。
Method、URL、Pathを設定してSendボタン。
それだけでリクエストを送って、返ってきたレスポンスが表示されます。

01.png

すごい簡単。

リクエストメソッドはGET/HEAD/POST/PUT/DELETE/OPTIONS/PATCHと各種対応。
BASIC認証とAuthorization: Bearerによる認証にも対応しているので、開発中で閉じてるサイト内にもアクセス可能。
非常に簡単にテストが行えますね。

WebSocket

なぜかWebSocketも使えます。
デフォルトではあらゆるメッセージを鸚鵡返しにするwss://echo.websocket.orgが指定されているので簡単にお試しができます。
WebSocketサーバ実装する人がこのWebアプリを使う場面というのはあんまり想像できませんが。

見た目を変えよう

デフォルトだと昔のハッカー気取りサイトみたいな黒緑です。
というかなんでアイコンがこんな可愛くないグレイなんですかね。

右上の歯車ボタンをクリックすると、背景色と一部文字色を変更可能です。

02.png

残念ながら任意の文字色を入れたりはできないようですが、まあそこまでもカスタマイズが必要なサービスでもないでしょう。

何度も同じリクエストを送りたい

Historyに履歴が残ります。
鉛筆みたいなボタンを押すとその履歴が反映されるので、再送も簡単です。

オンラインだと機密情報が…

GitHubcloneしてnpm installしてnpm run devしてlocalhost:3000にアクセスするんだよ。

PWAもあるぞ

一番右下のボタンからインストールできます。

03.png

宗教上の理由でインストールしていないので詳細は不明。

感想

本格的にテストを書くならやはり専用テストツールの導入が必要になると思いますが、とりあえずちょっとAPIの動作を試してみたいだけなんだ的な用途には最適だと思います。
使い方も見たらわかるレベルですからね。

GitHubでは開発の手伝いを募集しているようです。
またコーヒーも求めているので、使って気に入った人はぜひ投げ銭を送ってあげましょう。

【PHP8.0】未定義変数へのアクセスが例外にな・・・らない

$
0
0

ついにこの日が来てしまったようです。

PHPのユルさの象徴のひとつとして『未定義変数に普通にアクセスできる』というものがあります。

echo $a; // Notice: Undefined variable: a

大抵の言語ではエラーや例外で落ちますが、PHPでは処理が中断することはありません。
警告は出ますが、最もエラーレベルの低いE_NOTICEです。

が、PHP8.0でこの挙動が変わることになりそうです。

Reclassifying engine warningsというRFCが提出され(提出者はいつものNikita)、2019/09/26まで投票中です。
これは様々な警告のエラーレベルを変更しようというRFCなのですが、ここではそのうちのUndefined variable、Undefined array indexに絞って紹介してみます。
RFCでもこれらの項目は特別に分けられていますしね。

Reclassifying engine warnings

変更には投票の2/3の賛成が必要です。

Proposal

警告や例外には主にE_NOTICE、E_WARNING、Error例外の3段階がありますが、一部の警告についてエラーレベルを変更します。
ただし上げる場合でも1段階しか上げません。
現在E_NOTICEの警告のエラーレベルを上げる場合はE_WARNINGに上げるだけで、いきなり例外まで上げるとはない、ということです。

ただしUndefined variableのみ例外。

Undefined variable

大抵の場合、未定義変数へのアクセスは重大なバグです。
現在のE_NOTICEというとても軽い警告は、register_globalsのような外から変数を定義してしまうことができていたPHP暗黒時代の遺物です。
理想的には、未定義変数へのアクセスはコンパイルエラーになるべきですが、PHPの性質上コンパイル時に全ての分析を行うことはできないため、このRFCでは代わりにError例外にすることを提案します。

しかしながら、例外にするとこれまでのように警告を無視することができなくなるため、大量の警告を抑制して運用しているようなレガシーコードは多大な改修が必要となります。
また一部には、未定義変数の使用は正当なコーディングスタイルであるとさえ考えている過激派も存在します。

これらの理由のため、Error例外にするか、E_WARNINGにするか、E_NOTICEのままにするかの個別投票を行います。

2019/09/14現在、Error例外が22票、E_WARNINGが8票、E_NOTICEのままが4票で、おそらくError例外になります。

Undefined array index

未定義変数や未定義プロパティと同様、未定義の配列キーへのアクセスは、モダンPHPではプログラミングのエラーであるはずです。
しかし、変数やプロパティは主に静的に定義されるのに対し、配列のキーは動的に生成されることが多いため、全く同じ扱いをすることは難しいかもしれません。

JavaScriptのような一部の言語は、未定義の配列キーへのアクセスをエラーにしませんし、そのような操作をサイレントに実行します。
PHPでも主流ではないものの、同じようなコーディングスタイルを取る者たちがいて、彼らは未定義の配列キーへのアクセスが抑制可能な警告であることを願っています。

従って個別投票により、E_WARNINGに上げるか、E_NOTICEのままかを決定します。

2019/09/14現在、E_WARNINGが23票、E_NOTICEのままが13票で拮抗しています。

感想

私としては、仕事とかで書くちゃんとしたコードは常にerror_reporting(-1)と書いているので、業務上は影響ありません。
ただ使い捨てで適当に書き散らすコードなんかは警告が出てても一回動けばいいやとかやったりしているのですが、今後はそのような手抜きが許されなくなります。
書いたらなんかとりあえず動く、という他言語に対するPHPの大きな利点(にして同時に欠点)を投げ捨ててまで得られるものがあるのかというと、個人的には少々疑問に感じます。

巷に溢れるテキトーなコードも大半が即死し、PHPを始める敷居が大きく上がってしまうことは間違いありません。
テキトーなコードなんて死滅してもいい?
むしろ絶滅させるべき?
みんな正しいコードを書くべき?
正しいコードしか許されないべき?

未定義変数アクセスの例外化は、プログラミング言語として正しい方向性でしょう。
しかし世の中、正しければ勝てるというわけでもありません
使用前の変数宣言なんて難しいコーディング、誰も彼もができるわけではないのです。
この正しい変更は、入口が狭まるというデメリットを上回るメリットを果たして得られるのでしょうか。

あと最大の問題点として、が不可能になります。
こりゃ致命的だ。

回避策はあるか?

レガシーコードではerror_reporting(E_ALL~E_NOTICE)なんてことをやって警告の通知を無視していたわけですが、例外になるとこれが無視できなくなります。
プログラムを変更せずに回避する方法はないでしょうか?

PHPには例外の挙動を変更するset_exception_handlerという関数があります。
しかし、これは実のところ単にプログラム全体を囲むtry/catchなので、発生自体を無視したり、発生箇所に戻って先に進むといったことはできません。

ということでちょっと思いつかないですね。
誰かがきっと考えてプルリクしてくれるはず。

うん、まあ、そもそも例外が出ないように修正しろよって話ですが。

ニコニコ大百科のPHP問題を解いてみる

$
0
0

ニコニコ大百科のPHPの項目になんか問題があったので解いてみる。

いまいち文意がはっきりしない問題が多いので、そのへんは適当にどうにかしています。
あと問題文の英数字に半角全角が混ざってるのがとてもアレ。

初級編

問1:2つの変数、$val1$val2を、if式を使わずに比較して、大きい値を$largerを格納する式を一行で書いてみてね。

$larger = max($val1, $val2);

ヒントには三項演算子を使えとか書いてあるのだけど、$larger = $val1 > $val2 ? $val1 : $val2;とか書くより普通に標準関数使った方が楽ですね。

問2:改行コードを含む文字列に対し、改行コードに何が含まれているか調べるにはどうしたらよいか答えてね。

echo strpos($text, "\r\n") !== false ? 'CRLF' : ( strpos($text, "\r") !== false ? 'CR' : (strpos($text, "\n") !== false ? 'LF' :'無い'));

なんか題が意図しているものと違う気がしてならない。

問3:変数$objに、配列を使わずに$name,$age,$addressの三種類の値を入れて取り出せるようにしてね。

$obj = new class('安部菜々', 17, 'ウサミン星') {
    public $name, $age, $address;
    public function __construct($name, $age, $address) {
        $this->name = $name;
        $this->age = $age;
        $this->address = $address;
    }
};

echo $obj->name; // 安部菜々

たしかに配列は使ってないけどほぼ同じだろ。これでいいのか?

中級編

問4:小数点2の桁を丸める定義関数、roundmath()を作ってね。ただし、正数、負数どちらにも対応できるようにね。

function roundmath($num){
    return round($num, 1);
}

echo roundmath(12.35); // 12.4

正数、負数どちらにも対応できるようにね

とわざわざ太字で書いてあるんだけど、roundは普通に負数を扱えるので何を意図した主張なのかよくわからず。

問5:任意の行数$columnだけあるテーブル"HOGE"に対するSQLのInsert文で、一行でプレースホルダを埋め込んだ式を作成してみてね。なお、プレースホルダの記号は何を使ってもOKです。

え…これ難しくね?

((new PDO($dsn, $user, $pass))->prepare('INSERT INTO HOGE (id, value) VALUES(:id, :value)'))->execute([':id' => $id, ':value' => $value]);

そんなでもなかった。
行数はINSERTには全く関係ないので、あえて記載している意味がわからない。

問6:$desc_number = function(){ echo $number--;}; この無名関数に少しだけ手を加えて、関数desc_number()を実行するたびに、数を減らしていくようにしてね。なお、$numberには最初に100だけ代入されています。

$desc_number = function () {
    static $number = 100;
    echo $number--;
};

$desc_number(); // 100
$desc_number(); // 99

なんかこれも出題者が考えていた答えとちがう気がする。

上級編

問7:文字連結演算子( . )を使わずに、文字列の中に定数や今日の日付を埋め込んでみてね。

ヒントを見るに題意は変数展開でしょう。

    $d = date('Y年m月d日');
    $os = PHP_OS;
    echo "今日は${d}です。OSは${os}です。";

二重引用符では変数展開できますが、関数や定数は展開できないので1行では書けません。

まあ.を使わずに文字列結合すればいいだけなんですけどね。

printf('今日は%1$sです。OSは%2$sです。',  date('Y年m月d日'), PHP_OS);
echo '今日は', date('Y年m月d日'), 'です。OSは', PHP_OS, 'です。';

ちなみに2行使っていいなら面白い書き方があります。

$i = function ($v) { return $v; };
echo "今日は{$i(date('Y年m月d日'))}です。OSは{$i(PHP_OS)}です。";

参考:PHPで文字列リテラルに式展開

問8:同じページ内で定義関数function hogehoge($val1)function hogehoge($val2)を呼び出せるよようにしてみてね。ただし、クラスのメンバに入れるのは禁止です。

ヒントから察するに名前空間で分けろってことなのだろうか?

namespace A {
    function hogehoge($val1)
    {
        echo 'A:hogehoge' . $val1;
    }
}

namespace B {
    function hogehoge($val2)
    {
        echo 'B:hogehoge' . $val2;
    }
}

namespace {
    \A\hogehoge('A'); // A:hogehogeA
    \B\hogehoge('B'); // B:hogehogeB
}

でもこれ単に\A\hogehoge\B\hogehogeを作ってるだけであって、hogehogeを二つ作っているわけではないよね。

つまり、これが正解。

function hogehoge($val1) {
    echo 'hogehoge' . $val1;
}
hogehoge('A'); // hogehogeA

uopz_del_function('hogehoge');

function hogehoge($val2) {
    echo 'hogehoge' . $val2;
}
hogehoge('B'); // hogehogeB

うん、まあ絶対違うね。

問9:異なる要素数の2つの配列$ary1$ary2を同時にループして、順番に値を取り出したい。foreachを使って取り出してみてね。ただし、NULLを出さないでね。

順番にってのはどういうことだろうか。
$ary1の1番目、$ary2の1番目、$ary1の2番目、$ary2の2番目、って順でいいんかな?
あと余りは後ろにまとめればいいのか?

function array_mix(array $ary1, array $ary2){
    $multipleIterator = new MultipleIterator(MultipleIterator::MIT_NEED_ANY);
    $multipleIterator->attachIterator(new ArrayIterator($ary1), 1);
    $multipleIterator->attachIterator(new ArrayIterator($ary2), 2);
    foreach ($multipleIterator as $v1) {
        foreach ($v1 as $v2) {
            if ($v2 !== null) {
                yield $v2;
            }
        }
    }
}

foreach (array_mix([1, 2], [3, 4, 5, 6]) as $k => $v) {
    echo $v; // 132456
}
foreach (array_mix([1, 2, 3, 4], [5, 6]) as $k => $v) {
    echo $v; // 152634
}

むつかしかった。

問10:一つのメソッドを使うだけで、全10学級、国数英社理のテスト結果に対して、各生徒の偏差値と学年順位、学級順位を返すシステムを作ってね(横暴)。

1メソッド中にだばだば書いていけば特に問題なく実装できますね。
もちろんメンテコストなんてものは度外視だ。

ヒントはトレイトを使えとのこと。トレイト???

動かしてないけどこんなかんじでいけるやろ
class Grades{

    public static function getRank(array $students){
        // 教科毎の母集団作成
        $grades = $ranks = [];
        foreach ($students as $class => $classStudents) {
            foreach ($classStudents as $number => $student) {
                foreach ($student as $grade => $score) {
                    $grades[$grade]['cnt']++;
                    $grades[$grade]['total'] += $score;
                    $ranks[$grade][$class . '_' . $number] = $score;
                }
            }
        }

        // 順位
        foreach ($ranks as $subject => $grades) {
            $cnt = 1;
            arsort($grades);
            foreach ($grades as $grade => $score) {
                [$class, $number] = explode('_', $grade, 2);
                $students[$class][$number][$subject . '_rank'] = $cnt++;
            }
        }

        // 偏差値とか。疲れたのであとよろ
        return $students;
    }
}

$students = [ /* [クラス=>[学生番号=>[成績]]] */
    1 => [
        1 => [
            'Japanese' => 100,
            'English' => 90,
            'Mathematics' => 80,
            'Sociaty' => 70,
            'Science' => 60,
        ],
        ...
    ],
    ...
];

$result = Grades::getRank($students);

むしろトレイトをどう使えばいいのかわからなかった。

感想

設問が曖昧でよくわからない問題があるのを別にしても、明らかに出題者が想定してない解法を書いてしまってる問いが多々あるわけですがまあこんなものでどうでしょう。

【Laravel5.8】テストでLaravel外にリクエストを投げられない

$
0
0

Laravelのテストでは頻繁に$response = $this->get($url);とか書くと思いますが、実はこれ、実際にリクエストを送信してはいません。
内部で何かごちゃごちゃとやっていて、一見リクエストを送ったように見せかけてレスポンスを返してきています。

その実害が前起こったlaravel_sessionが取れないというものですが、他にも$_SERVER['REQUEST_URI']などの環境変数を直接使っているとテスト時に値が入ってこないので死にます。
まあ、そちらはRequest::getPathInfo()などを使うべきだということでしょう。

本題はここからで、実はgetやpostは、外部サーバにリクエストを飛ばすことができません。
$this->get('https://example.com/a/b');って打っても、ドメイン部分は完全に無視されてa/bに飛びます。
なんで。

さらにLaravelをサブディレクトリ運用している場合、その外側に行くこともできません。
Laravelをhttps://example.com/laravel/の中で運用していたとして、https://example.com/other/にすら行けないということです。
$this->get('https://example.com/other/foo/bar');とすると、実際はhttps://example.com/laravel/other/foo/barに行ってしまいます。
なんで。

内部リクエストではなく、実際に該当URLにHTTPリクエストを送る方法はないでしょうか。
URLの作成はMakesHttpRequestsHttpFoundation\Requestのあたりで行われているのですが、読んでも全く理解できませんでした。

Laravel Duskというパッケージはあるのですが、ちょっと思ったのと違います。
これはWeb画面を表示してテキストを入力してボタンを押してといったテストを行えるようになるヘッドレスブラウザというものです。

いや、そんな高機能な代物を求めてるわけじゃないんだ。
ただただ単に外部サーバにちょっくらGETしたいだけなんだ。

こういう場合どうするのがベストな方法なんだろうか?
やっぱGuzzleとか使って手動?

Bash初心者からエキスパートになるためのコマンドとヒント101

$
0
0

以下はAndrewによる記事、101 Bash Commands and Tips for Beginners to Expertsの日本語訳です。

一部を除き、上から順にコマンドを打って確かめることができるようになっています。
読むだけではなく、実際に打って試してみることで理解が早まることでしょう。

101 Bash Commands and Tips for Beginners to Experts

一年前まで、私はもっぱらMacOSとUbuntuのふたつのOSで作業をしていました。
両OSにおいて、私のデフォルトシェルはbashです。
過去6、7年ほどbashで仕事をしているため、bashがどのように動作するか、ある程度は理解しているつもりです。
従って、bashを始めたばかりの人にとって一般的で有用なコマンドについて、いくつか解説していきたいと思います。
また、bashについて知っているべきことを全て知っていると思っている人も、とりあえず見ていってください。
あなたが忘れているかもしれない、使うことで仕事がより簡単になるかもしれない、TIPSやリマインダのヒントを鏤めました。

以下のコマンドは段階を踏んで進んでいくので、bashを使い始めたばかりであっても、最初から最後まで順番に進んでいくことができます。
最初は簡単で、徐々に難しく一般的ではないコマンドを出していきます。

The Basics

First Commands, Navigating the Filesystem

最初のコマンドはファイルシステムのナビゲーション。

最近のファイルシステムはディレクトリツリー構造を持っています。
ディレクトリは親のないルートディレクトリ、もしくは一つの親を持つサブディレクトリのいずれかです。
ディレクトリツリーを逆方向(子から親へ)移動し続けると、最終的には必ずルートディレクトリに到達します。
一部のファイルシステムには複数のルートディレクトリを持つものもありますが(WindowsのC:\A:\など)、UnixやUnixライクOSには単一のルートディレクトリ/しかありません。

pwd / ls / cd

ファイルシステム内で作業する場合、ユーザは常にどこかのディレクトリ、すなわちカレントディレクトリもしくは作業ディレクトリと呼ばれる場所にいます。
カレントディレクトリはpwdコマンドで確認することができます。

[ andrew@pc01 ~ ]$ pwd
/home/andrew

カレントディレクトリの中身、ファイルや子ディレクトリなどを参照するのはlsコマンドです。

[ andrew@pc01 ~ ]$ ls
Git  TEST  jdoc  test  test.file

おまけ
ls -aで隠しファイル(.で始まるなど)を表示する。
ls -lでファイルの詳細も表示する。
ls -l -aのように複数のオプションを連ねられる。
ls -l -aのかわりにls -laと書くこともできる。

カレントディレクトリを変更するのはcdコマンドです。

[ andrew@pc01 ~ ]$ cd TEST/

[ andrew@pc01 TEST ]$ pwd
/home/andrew/TEST

[ andrew@pc01 TEST ]$ cd A

[ andrew@pc01 A ]$ pwd
/home/andrew/TEST/A

cd ..で親ディレクトリに移動することができます。

[ andrew@pc01 A ]$ cd ..

[ andrew@pc01 TEST ]$ pwd
/home/andrew/TEST

cd ~あるいは単にcdで、どこからでもユーザのホームディレクトリ(通常は/home/username)に戻ってきます。

[ andrew@pc01 TEST ]$ cd

[ andrew@pc01 ~ ]$ pwd
/home/andrew

おまけ
cd ~userは、"userのホームディレクトリにcd"という意味。
cd ../..のように一気に多段ディレクトリ移動ができる。
ひとつ前にいたディレクトリに戻るのはcd -
.はカレントディレクトリのこと。よってcd .は何もしない。

; / && / &

コマンドラインに入力するのはコマンドと呼ばれるもので、そしてコマンドはPCのどこかに保存されているプログラムを実行させます。
このプログラムはLinuxのビルトインコマンドであることもあれば、誰かが作ったアプリであることもあり、そしてあなたが作ったコードであるかもしれません。
時折、複数のコマンドを連続して実行したいことがあります。
そのためにはセミコロン;を使います。

[ andrew@pc01 ~ ]$ ls; pwd
Git  TEST  jdoc  test  test.file
/home/andrew

上のコマンドは、まずlsでカレントディレクトリの中身を表示し、次いでpwdでカレントディレクトリの位置を表示します。

コマンドを連続実行するためのもうひとつ便利なツールが&&です。
これを使うと、左側のコマンドが失敗した場合は右のコマンドが実行されません。
;&&は連続で使うことができます。

# cdでtypoしたのでpwd以降は実行されない
[ andrew@pc01 ~ ]$ cd /Giit/Parser && pwd && ls && cd
-bash: cd: /Giit/Parser: No such file or directory

# 成功したので後続のコマンドも実行される
[ andrew@pc01 ~ ]$ cd Git/Parser/ && pwd && ls && cd
/home/andrew/Git/Parser
README.md  doc.sh  pom.xml  resource  run.sh  shell.sh  source  src  target

;を使った場合は、手前のコマンドが失敗したとしても後ろのコマンドが実行されます。

# pwdに失敗したけどpwdやlsが動く
[ andrew@pc01 ~ ]$ cd /Giit/Parser ; pwd ; ls
-bash: cd: /Giit/Parser: No such file or directory
/home/andrew
Git  TEST  jdoc  test  test.file

&は一見&&と似たように見えますが、実際は全く異なる機能です。
実行に長い時間がかかるコマンドを実行すると、そのコマンドの実行が終了するまでコマンドラインは何もできなくなります。
コマンドの後ろに&を書くと待ちが発生しなくなり、古いコマンドがまだ動いている状態でも、次のコマンドを実行することができるようになります。

[ andrew@pc01 ~ ]$ cd Git/Parser && mvn package & cd
[1] 9263

おまけ
&を使ってコマンドを隠すことを、我々はジョブ(あるいはプロセス)のバックグラウンド化と呼んでいる。
現在バックグラウンド状態のジョブはjobsコマンドで確認することができる。

[ andrew@pc01 ~ ]$ jobs
[1]+ Running cd Git/Parser/ && mvn package &

Getting Help

-h

ほとんどのコマンドは、-hあるいは--helpでコマンドのヘルプを表示してくれます。

[ andrew@pc01 ~ ]$ du --help
Usage: du [OPTION]... [FILE]...
  or:  du [OPTION]... --files0-from=F
Summarize disk usage of the set of FILEs, recursively for directories.

Mandatory arguments to long options are mandatory for short options too.
  -0, --null            end each output line with NUL, not newline
  -a, --all             write counts for all files, not just directories
      --apparent-size   print apparent sizes, rather than disk usage; although
                          the apparent size is usually smaller, it may be
                          larger due to holes in ('sparse') files, internal
                          fragmentation, indirect blocks, and the like
  -B, --block-size=SIZE  scale sizes by SIZE before printing them; e.g.,
                           '-BM' prints sizes in units of 1,048,576 bytes;
                           see SIZE format below
...

man

大抵のコマンドは、manコマンドに続けて打つとコマンドのマニュアルを表示することができます。
qでマニュアルを終了します。

LS(1)                            User Commands                           LS(1)

NAME
       ls - list directory contents

SYNOPSIS
       ls [OPTION]... [FILE]...

DESCRIPTION
       List  information  about  the FILEs (the current directory by default).
       Sort entries alphabetically if none of -cftuvSUX nor --sort  is  speci-
       fied.

       Mandatory  arguments  to  long  options are mandatory for short options
       too.
...

Viewing and Editing Files

head / tail / cat / less

headはファイルの最初の数行を出力します。
出力行数のデフォルトは10で、-nで変更できます。

# 最初の3行を表示
[ andrew@pc01 ~ ]$ head -n 3 c
this
file
has

tailは逆にファイルの最後の数行を出力します。
出力行数はheadと同じように指定するか、もしくは-n +NでN行目から最後までを出力できます。

# 4行目から最後までを表示
[ andrew@pc01 ~ ]$ tail -n +4 c
exactly
six
lines

catは複数のファイルを連結し、その結果を標準出力(通常はターミナル)に送信します。
しかしこのコマンドは、複数のファイルや、単に1ファイルの中身をさっと確認するために使われることがしばしばあります。
そのような使い方をすると猫の無駄遣いで告発される可能性がありますが、たいした害はないので気にしなくてもかまいません。

[ andrew@pc01 ~ ]$ cat a
file a

[ andrew@pc01 ~ ]$ cat a b
file a
file b

ファイルを素早く表示するもうひとつの方法がlessで、これを使うと読み取り専用のvimのようなウィンドウが開きます。
実はmoreというコマンドもあるのですが、lessのほうが優位なので出番はありません。
もっと詳しく(more)、いや少なく?(less)知りたい場合は、lessmoreのmanページを参照してください。

nano / nedit

nanoは必要最小限のコマンドラインテキストエディタです。
初心者や、100万のショートカットを覚えたくないユーザにとっては最適の優れたエディタです。
私のキャリアにおいても、最初の数年間はnanoだけで十分でした。
nanoで独自のシンタックスハイライトを定義するのは大変なので、さすがにそろそろ別の強力なエディタを検討し始めていますが。

neditは小規模なグラフィカルテキストエディタで、X Windowを開き、クリック編集、ドラッグアンドドロップ、構文強調表示等が可能です。
スクリプトに小さな変更を加えながら何度も実行したいとき、私はよくneditを使用します。

その他の一般的なCLI/GUIテキストエディタとして、emacsvivimgeditNotepad++Atom、そしてその他たくさんの選択肢が存在します。
私が触った中でも特に魅力的で推奨できると感じたものは、MicroLight Table、そしてVS Codeです。

最近のエディタは全て、検索、置換、構文強調表示などの便利機能を標準装備しています。
viemacsnanoneditより遙かに多くの機能がありますが、同時に学習曲線も急坂です。
エディタを色々試してみて、自分に合ったものを探してみてください。

Creating and Deleting Files and Directories

ファイル・ディレクトリの作成と削除。

touch

touchはファイルのタイムスタンプを更新するために作られたものですが、空のファイルを作るためにも利用できます。
nanoなどのテキストエディタで新規ファイルを作成してみましょう。

[ andrew@pc01 ex ]$ ls

[ andrew@pc01 ex ]$ nano a

/* ここで保存してエディタを終了 */

[ andrew@pc01 ex ]$ ls
a

touchなら1コマンドです。

[ andrew@pc01 ex ]$ touch b && ls
a  b

おまけ
Ctrl+zで現在実行中のプロセスをバックグラウンドにできる。
fgコマンドでプロセスに戻る。

[ andrew@pc01 ex ]$ nano a

/* ここでCtrl + z */

Use fg to return to nano

[1]+ Stopped nano a
[ andrew@pc01 ex ]$ fg

/* 編集画面に戻った */

さらにおまけ
Ctrl+cだと現在実行中のプロセスを強制終了する。
バックグラウンドプロセスを強制終了するにはkill %N
Nに入れるプロセス番号はjobsコマンドで確認できる。

mkdir / rm / rmdir

空のディレクトリを作るのはmkdirコマンドです。

[ andrew@pc01 ex ]$ ls && mkdir c && ls
a  b
a  b  c

rmでファイルを削除できますが、削除すると元に戻せません。
注意してください。

[ andrew@pc01 ex ]$ rm a && ls
b  c

削除確認の警告を行いたい場合は-iオプションを追加しましょう。

[ andrew@pc01 ex ]$ rm -i b
rm: remove regular empty file 'b'? y

空のディレクトリを削除するのはrmdirです。
空のディレクトリでls -aすると、カレントディレクトリ.と親ディレクトリ..だけが表示されます。

[ andrew@pc01 ex ]$ rmdir c && ls -a
.  ..

rmdirでは、空でないディレクトリは削除できません。

[ andrew@pc01 ex ]$ cd .. && ls test/
*.txt  0.txt  1.txt  a  a.txt  b  c

[ andrew@pc01 ~ ]$ rmdir test/
rmdir: failed to remove 'test/': Directory not empty

中身の入ったディレクトリを全て強制的に削除するには、rm -rfを使います。
-rは再帰、-fは強制実行するオプションです。

[ andrew@pc01 ~ ]$ rm –rf test

Moving and Copying Files, Making Links, Command History

ファイルの移動、コピー、リンク、コマンド履歴。

mv / cp / ln

ファイルの移動、リネームはmvです。
ディレクトリを移動してファイル名をそのままにする、ファイル名もついでに変更するといったことができます。

[ andrew@pc01 ex ]$ ls && mv a e && ls
a  b  c  d
b  c  d  e

コピーはcpです。

[ andrew@pc01 ex ]$ cp e e2 && ls
b  c  d  e  e2

lnはファイルへのハードリンクを作成します。

# 第一引数が対象ファイル、第二引数がリンク名
[ andrew@pc01 ex ]$ ln b f && ls
b  c  d  e  e2  f

ln -sでシンボリックリンクが作成できます。

[ andrew@pc01 ex ]$ ln -s b g && ls
b  c  d  e  e2  f  g

ハードリンクは同じメモリを参照するひとつのファイルへの別名を作ります。
シンボリックリンクは元のファイル名を追跡するだけで、ファイル実体を参照するのは元ファイルだけです。
これ以上の詳細についてはWhat is the difference between a symbolic link and a hard link?を見てください。

Command History

bashは、実行したコマンドを再実行するために役立つ機能をふたつ持っています。
ひとつめの機能はタブ補完です。
コマンドの一部を入力したところでTABを押すと、何を打とうとしているのか端末が推測してくれます。

[ andrew@pc01 dir ]$ ls <ENTER>
anotherlongfilename  thisisalongfilename  anewfilename

[ andrew@pc01 dir ]$ ls t <TAB>

TABを押すと、以下のようにコマンドが最後まで補完されます。

[ andrew@pc01 dir ]$ ls thisisalongfilename <ENTER>
thisisalongfilename

特定しきれない場合はTABを何度か押すことになります。

[ andrew@pc01 dir ]$ ls a <TAB>

[ andrew@pc01 dir ]$ ls an <TAB>
anewfilename  anotherlongfilename

bashは以前に実行したコマンドを幾つか覚えており、Ctrl+Rでその履歴を検索することができます。

(reverse-i-search)`':

ここでanewと入力すると、その文字が含まれるコマンドのうち最後に実行したものが出てきます。

(reverse-i-search)`anew': touch anewfilename

Directory Trees, Disk Usage, and Processes

ディレクトリツリー、ディスク使用量、プロセス。

mkdir –p / tree

mkdirはデフォルトではひとつのディレクトリのみを作成します。
どういうことかというと、ディレクトリd/eが存在しないときにディレクトリd/e/fを作ることはできないということです。

[ andrew@pc01 ex ]$ ls && mkdir d/e/f
a  b  c
mkdir: cannot create directory 'd/e/f': No such file or directory

-pオプションを付けることで、深い階層のディレクトリも一気に作成してくれます。

[ andrew@pc01 ex ]$ mkdir -p d/e/f && ls
a  b  c  d

treeコマンドはディレクトリツリーをいいかんじにビジュアル化して表示してくるので、ディレクトリ構造を把握するのに役立ちます。
デフォルトでは指定ディレクトリ以下のディレクトリツリーを全て示しますが、-Lオプションで階層を限定することもできます。

[ andrew@pc01 ex ]$ tree -L 2
.
|-- a
|-- b
|-- c
`-- d
    `--e

3 directories, 2 files

空のディレクトリを表示させたくないときは--pruneオプションを使います。
このオプションは再帰的なので、空のディレクトリだけが入った空でないディレクトリも消えることに注意しましょう。

[ andrew@pc01 ex ]$ tree --prune
.
|-- a
`-- b

df / du / ps

dfはディスクおよびシステムの容量がどれだけ使用されているかを一覧表示します。

[ andrew@pc01 ex ]$ df -h
Filesystem                   Size  Used Avail Use% Mounted on
udev                         126G     0  126G   0% /dev
tmpfs                         26G  2.0G   24G   8% /run
/dev/mapper/ubuntu--vg-root  1.6T  1.3T  252G  84% /
...

このコマンドにおいては、-hオプションは"ヘルプ表示"ではなく"人間が見てわかりやすく表示"を意味します。
いくつかのコマンドは、このオプションを使うことで、バイト数そのままではなくキロバイトK、ギガバイトGなどでファイルやディスク容量を出力してくれます。
duは指定ディレクトリとそのサブディレクトリの使用量を表示します。
あるハードディスクの使用量を知りたい場合はdf、あるディレクトリの使用量を知りたいときはduを使います。

[ andrew@pc01 ex ]$ du
4       ./d/e/f
8       ./d/e
12      ./d
4       ./c
20      .

du--max-depth=Nオプションを受け取り、指定した階層のディレクトリまでを表示します。

[ andrew@pc01 ex ]$ du -h --max-depth=1
12K     ./d
4.0K    ./c
20K     .

psはユーザが実行中の全てのプロセス(ジョブ)を表示します。

[ andrew@pc01 ex ]$ ps
  PID TTY          TIME CMD
16642 pts/15   00:00:00 ps
25409 pts/15   00:00:00 bash

Miscellaneous

passwd / logout / exit

パスワード変更コマンドはpasswdです。
確認のために現在のコマンドを一回と、変更後のパスワードを2回入力する必要があるため、入力ミスすることはないでしょう。

[ andrew@pc01 dir ]$ passwd
Changing password for andrew.
(current) UNIX password:    <現在のパスワード>
Enter new UNIX password:    <新パスワード>
Retype new UNIX password:   <新パスワードを再入力>
passwd: password updated successfully

logoutで現在のログインシェルが終了します。

[ andrew@pc01 dir ]$ logout

──────────────────────────────────────────────────────────────────────────────
Session stopped
    - Press <return> to exit tab
    - Press R to restart session
    - Press S to save terminal output to file

exitはサブシェルを含め全て終了します。

[ andrew@pc01 ~ ]$ exit
logout

──────────────────────────────────────────────────────────────────────────────
Session stopped
    - Press <return> to exit tab
    - Press R to restart session
    - Press S to save terminal output to file

clear / *

clearを実行するとカーソルが画面の一番上に移動します。
実際やってることは現在の行の下に空白行を追加しているだけですが、ワークスペースをクリアするのに有用です。

ファイルの検索にはglob(*、ワイルドカード、Kleene Star)が便利です。
以下の2コマンドの違いについて注意してください。

[ andrew@pc01 ~ ]$ ls Git/Parser/source/
PArrayUtils.java     PFile.java            PSQLFile.java      PWatchman.java
PDateTimeUtils.java  PFixedWidthFile.java  PStringUtils.java  PXSVFile.java
PDelimitedFile.java  PNode.java            PTextFile.java     Parser.java

[ andrew@pc01 ~ ]$ ls Git/Parser/source/PD*
Git/Parser/source/PDateTimeUtils.java  Git/Parser/source/PDelimitedFile.java

globは1コマンド中に何度も使用することができ、"0文字以上の文字列"に一致します。

[ andrew@pc01 ~ ]$ ls Git/Parser/source/P*D*m*
Git/Parser/source/PDateTimeUtils.java  Git/Parser/source/PDelimitedFile.java

Intermediate

Disk, Memory, and Processor Usage

ncdu

ncdu(NCurses Disk Usage)はファイル使用量の概要を表示する、改善版duのようなコマンドです。
読み取り専用のvimのようなウィンドウが開くので、終了する際はqを押しましょう。

[ andrew@pc01 ~ ]$ ncdu

ncdu 1.11 ~ Use the arrow keys to navigate, press ? for help
--- /home/andrew -------------------------------------------------------------
  148.2 MiB [##########] /.m2
   91.5 MiB [######    ] /.sbt
   79.8 MiB [#####     ] /.cache
   64.9 MiB [####      ] /.ivy2
   40.6 MiB [##        ] /.sdkman
   30.2 MiB [##        ] /.local
   27.4 MiB [#         ] /.mozilla
   24.4 MiB [#         ] /.nanobackups
   10.2 MiB [          ]  .confout3.txt
    8.4 MiB [          ] /.config
    5.9 MiB [          ] /.nbi
    5.8 MiB [          ] /.oh-my-zsh
    4.3 MiB [          ] /Git
    3.7 MiB [          ] /.myshell
    1.7 MiB [          ] /jdoc
    1.5 MiB [          ]  .confout2.txt
    1.5 MiB [          ] /.netbeans
    1.1 MiB [          ] /.jenv
  564.0 KiB [          ] /.rstudio-desktop
 Total disk usage: 552.7 MiB  Apparent size: 523.6 MiB  Items: 14618

top / htop

topは、現在実行されている全てのプロセスと所有者、メモリ使用量などを表示します。
そしてhtopはインタラクティブな改良版topです。
いずれも-u usernameオプションで該当ユーザのプロセスだけを表示することができます。

REPLs and Software Versions

REPLs

REPLはRead-Eval-Print Loopの略で、コマンドラインと同じようなものですが、通常はプログラミング言語との対話的ウィンドウに対して呼ばれます。
PythonのREPLはpythonコマンドで始めることができ、quit()関数で終了します。

[ andrew@pc01 ~ ]$ python
Python 3.5.2 (default, Nov 12 2018, 13:43:14) ...
>>> quit()

RのREPLはRコマンドで始まり、q()関数で終了です。

[ andrew@pc01 ~ ]$ R
R version 3.5.2 (2018-12-20) --"Eggshell Igloo" ...
> q()
Save workspace image? [y/n/c]: n

ScalaのREPLはscalaコマンドで始まり、quitコマンドで終了です。

[ andrew@pc01 ~ ]$ scala
Welcome to Scala 2.11.12 ...
scala> :quit

JavaのREPLはjshellコマンドで始まり、/exitコマンドで終了です。

[ andrew@pc01 ~ ]$ jshell
| Welcome to JShell--Version 11.0.1 ...
jshell> /exit

これらのREPLはCtrl+dでも終了できます。
Ctrl+dはUnixのEOFであり、入力の終端を意味します。

-version / --version / -v

ほとんどのコマンドやプログラムは、そのソフトウェアバージョンを表示する-versionもしくは--versionオプションがあります。
ほとんどのアプリケーションにも同じオプションが用意されています。

[ andrew@pc01 ~ ]$ ls --version
ls (GNU coreutils) 8.25 ...

[ andrew@pc01 ~ ]$ ncdu -version
ncdu 1.11

[ andrew@pc01 ~ ]$ python --version
Python 3.5.2

ただし、稀にあまり馴染みのないオプションも存在します。

[ andrew@pc01 ~ ]$ sbt scalaVersion
...
[info] 2.12.4

バージョンを表すオプションとして-vを使うプログラムもあります。
しかし多くの場合-vは'verbose'を意味し、診断情報やデバッグ情報といった多くの詳細情報も出力します。

SCP(1)                    BSD General Commands Manual                   SCP(1)

NAME
     scp -- secure copy (remote file copy program)
...
-v      Verbose mode.  Causes scp and ssh(1) to print debugging messages
             about their progress.  This is helpful in debugging connection,
             authentication, and configuration problems.
...

Environment Variables and Aliases

Environment Variables

環境変数("env vars"と略されることもある)は、bashシェル上で作成・使用できる永続変数のことです。
これらは=で定義し、$で参照します。
pritenvで現在定義されている全ての環境変数を確認することができます。

[ andrew@pc01 ~ ]$ printenv
SPARK_HOME=/usr/local/spark
TERM=xterm
...

新しい環境変数を定義しましょう。
=の前後にスペースを入れてはいけません。

[ andrew@pc01 ~ ]$ myvar=hello

定義した環境変数はechoで取得できます。
ただし変数の頭に$を付けます。

[ andrew@pc01 ~ ]$ echo $myvar
hello

スペースやその他の空白を含む値を指定したいときは、値を"で囲まなければなりません。
また環境変数は警告無しに上書きできるので気をつけましょう。

[ andrew@pc01 ~ ]$ myvar="hello, world!" && echo $myvar
hello, world!

環境変数をexportコマンド付きで定義することもできます。
そうすると、サブシェル内でもその環境変数を参照することができるようになります。

[ andrew@pc01 ~ ]$ export myvar="another one" && echo $myvar
another one

環境変数の設定を解除するには、=の右側を空欄にして決定するか、unsetコマンドを使用します。

[ andrew@pc01 ~ ]$ unset mynewvar

[ andrew@pc01 ~ ]$ echo $mynewvar

Aliases

エイリアスは環境変数に似ていますが、普通は異なる目的で利用されます。
長いコマンドを短く置き換えるために使用されます。

[ andrew@pc01 apidocs ]$ ls -l -a -h -t
total 220K
drwxr-xr-x 5 andrew andrew 4.0K Dec 21 12:37 .
-rw-r--r-- 1 andrew andrew 9.9K Dec 21 12:37 help-doc.html
-rw-r--r-- 1 andrew andrew 4.5K Dec 21 12:37 script.js
...

[ andrew@pc01 apidocs ]$ alias lc="ls -l -a -h -t"

[ andrew@pc01 apidocs ]$ lc
total 220K
drwxr-xr-x 5 andrew andrew 4.0K Dec 21 12:37 .
-rw-r--r-- 1 andrew andrew 9.9K Dec 21 12:37 help-doc.html
-rw-r--r-- 1 andrew andrew 4.5K Dec 21 12:37 script.js
...

エイリアスの削除はunaliasです。

[ andrew@pc01 apidocs ]$ unalias lc

[ andrew@pc01 apidocs ]$ lc
The program 'lc' is currently not installed. ...

おまけ
環境変数とエイリアスの違い
一部のプログラムでは、そのプログラム専用のエイリアスを設定できる。gitの例

Basic bash Scripting

bash Scripts

bashスクリプト(シェルスクリプト)を使うと、複雑なプロセスを自動化し、再利用可能な形にパッケージ化することができます。
拡張子は通常.shです。
bashスクリプトには、通常のコマンドをいくらでも詰め込むことができます。

[ andrew@pc01 ~ ]$ echo "ls && touch file && ls" > ex.sh

作成したシェルスクリプトは、sourceもしくはshコマンドで実行可能です。

[ andrew@pc01 ~ ]$ source ex.sh
Desktop  Git  TEST  c  ex.sh  project  test
Desktop  Git  TEST  c  ex.sh  file  project  test

chmodコマンドで、シェルスクリプトを直接実行可能にすることができます。
詳しくは後で解説します。

[ andrew@pc01 ~ ]$ echo "ls && touch file2 && ls" > ex2.sh

[ andrew@pc01 ~ ]$ chmod +x ex2.sh

実行権限+xのついたシェルスクリプトは、頭に./を付けることで直接実行できます。

[ andrew@pc01 ~ ]$ ./ex2.sh
Desktop  Git  TEST  c  ex.sh  ex2.sh  file  project  test
Desktop  Git  TEST  c  ex.sh  ex2.sh  file  file2  project  test

長いスクリプトを書きたい場合、行の最後に\を入れることで改行して入力できます。

[ andrew@pc01 ~ ]$ echo "for i in {1..3}; do echo \
> \"Welcome \$i times\"; done" > ex3.sh

シェルスクリプトにはもちろん、ループや関数などの複雑な構造も仕込むことができます。

[ andrew@pc01 ~ ]$ source ex3.sh
Welcome 1 times
Welcome 2 times
Welcome 3 times

Custom Prompt and ls

bashスクリプティングは、あなたの人生をずっと楽に、豊かに、カラフルにしてくれます。
この素晴らしいチートシートをご覧ください。

ところでシェルのプロンプトに出てくる文字列も環境変数で定義されていて、その名前は$PS1です。
他にもプロンプトはあって、それらについてはこちらを見てください。

[ andrew@pc01 ~ ]$ printf "%q" $PS1
$'\\n\\[\E[1m\\]\\[\E[30m\\]\\A'$'\\[\E[37m\\]|\\[\E[36m\\]\\u\\[\E[37m\\]@\\[\E[34m\\]\\h'$'\\[\E[32m\\]\\W\\[\E[37m\\]|'$'\\[\E(B\E[m\\]‘

デフォルトのプロンプトは、exportコマンドで変更可能です。

[ andrew@pc01 ~ ]$ export PS1="\ncommand here> "

command here> echo $PS1
\ncommand here>

色を付けることもできます。

command here> export PS1="\e[1;31m\nCODE: \e[39m"

# (ここ本当は赤いんだけどMarkdownで色の付け方がわからんかった)
CODE: echo $PS1
\e[1;31m\nCODE: \e[39m

lsが出力する色も環境変数$LS_COLORSで変更することができます。

CODE: ls
Desktop  Git  TEST  c  ex.sh  ex2.sh  ex3.sh  file  file2  project  test

CODE: export LS_COLORS='di=31:fi=0:ln=96:or=31:mi=31:ex=92'

# (ここも本当は赤い)
CODE: ls
Desktop  Git  TEST  c  ex.sh  ex2.sh  ex3.sh  file  file2  project  test

Config Files

Config Files / .bashrc

色々とコマンドを実行して設定を変更しましたが、いちどログアウトして再度ログインすると、全ての変更がリセットされてしまうことに気付くでしょう。
設定ファイルを使用することで、ログインしなおしてもシェルや特定のプログラムの設定を保持することができます。
bashシェルの主な設定ファイルは~/.bashrcです。
~/.bashrcに追加したエイリアス、環境変数、エイリアスは、ログインするたびに使用可能になります。
~/.bashrcに書かれたコマンドはログイン時に実行されるということです。
~/.bashrcを更新した場合、sourceコマンドでログアウトせずに最新状態にすることができます。

[ andrew@pc01 ~ ]$ nano ~/.bashrc

そして最初にecho "~/.bashrc loaded!"という行を付け加えてみましょう。

[ andrew@pc01 ~ ]$ source ~/.bashrc
~/.bashrc loaded!

ログアウトしてログインし直してもこうなります。

Last login: Fri Jan 11 10:29:07 2019 from 111.11.11.111
~/.bashrc loaded!

[ andrew@pc01 ~ ]

Types of Shells

ログインシェルとは、ログインしたとき最初に起動するシェルです。
対話型シェルとは、コマンドを入力することができるシェルです。
シェルには対話型ログインシェル、非ログイン非対話型シェル、あるいは他の組み合わせなど様々なものがあります。

~/.bashrc以外にも、ログインしたときやログアウトした際に自動的に実行されるスクリプトは幾つか存在します。
例を挙げると、

/etc/profile
~/.bash_profile
~/.bash_login
~/.profile
~/.bash_logout
/etc/bash.bash_logout

これらのスクリプトのどれが有効で、どの順番で実行されるかは、シェルのタイプによって異なります。
詳細についてはbashのmanページや、Stack Overflowのポストなどを参照してください。

bashはsourceコマンドで別のスクリプトを読み込むことも可能です。
たとえば~/.bashrcに以下を記載することができます。

source ~/.bashrc_addl

これにより、.bashrc_addlsource対象のファイルとなります。
このファイルにも独自のエイリアス、関数、環境変数などを記述することができます。
同様に、さらに別のスクリプトをsourceすることもできます。
そうする場合はsourceが無限ループしないように注意してください。

機能やOS(Ubuntu、RedHat、macOS)などによってコマンドをファイルごとに分離しておくと役立つことがあります。

~/.bash_ubuntu …Ubuntu固有の設定
~/.bashrc_stylesPS1LS_COLORSなど外観の設定
~/.bash_java …言語ごとのの設定

外観やOS固有設定などをそれぞれ個別のファイルに切り出し、そしてそれらのうち必要なものをsourceするひとつの管理ファイルから構成します。
このような構成であれば、あらゆるマシンやOSで使用可能なシェルになります。

bash以外のシェルも存在することに気をつけてください。
bashは単にシェルの一種であるにすぎません。
他に一般的なシェルとしてはzshcshfishなどが存在します。
様々なシェルを試して、自分に合ったものを見つけてください。

ただし、このチュートリアルはbashシェルで記述されているため、他のシェルでは動作しない可能性があることに注意してください。

Finding Things

whereis / which / whatis

whereisは、あるコマンドに対して、おそらく有用であろうファイルを検索します。
そのコマンドのバイナリすなわち実行可能なコード、ソースファイル、マニュアルのmanページなどです。

[ andrew@pc01 ~ ]$ whereis ls
ls: /bin/ls /usr/share/man/man1/ls.1.gz

whichはバイナリ、すなわちコマンドの場所のみを返します。

[ andrew@pc01 ~ ]$ which ls
/bin/ls

whatisはmanページからコマンドの概要を1行表示します。

[ andrew@pc01 ~ ]$ whatis whereis which whatis
whereis (1)          - locate the binary, source, and manual page files for a command
which (1)            - locate a command
whatis (1)           - display one-line manual page descriptions

whichは、エイリアスで隠蔽されているコマンドのオリジナルを探すのに役立ちます。

[ andrew@pc01 ~ ]$ alias ls="ls -l"

# エイリアスのせいで元コマンドと挙動が違う
[ andrew@pc01 ~ ]$ ls
total 36
drwxr-xr-x 2 andrew andrew 4096 Jan  9 14:47 Desktop
drwxr-xr-x 4 andrew andrew 4096 Dec  6 10:43 Git
...

# whichで調べた元コマンドを直接叩けばオリジナルの結果が得られる
[ andrew@pc01 ~ ]$ /bin/ls
Desktop  Git  TEST  c  ex.sh  ex2.sh  ex3.sh  file  file2  project  test

locate / find

locateは、予め作成しておいたキャッシュリストを参照して高速にファイルを検索します。

[ andrew@pc01 ~ ]$ locate README.md
/home/andrew/.config/micro/plugins/gotham-colors/README.md
/home/andrew/.jenv/README.md
/home/andrew/.myshell/README.md
...

リストを検索するのでfindより高速ですが、必ずしも最新の状態と一致しているとは限りません。
findは実際のファイルシステムを検索して、探しているファイルを見つけます。
こちらは実ファイルを検索するため、常に最新のファイルを得ることができます。

[ andrew@pc01 ~ ]$ find ~/ -iname "README.md"
/home/andrew/.jenv/README.md
/home/andrew/.config/micro/plugins/gotham-colors/README.md
/home/andrew/.oh-my-zsh/plugins/ant/README.md
...

findは1971年という最初期のUNIXから実装されているため、1994年にGNUに追加されたlocateより遙かに広い環境で利用可能です。

findlocateよりずっと多くの機能を備えていて、更新日、サイズ、所有者、ファイル/ディレクトリ、タイムスタンプ、パーミッション、ディレクトリの深さなど多様な条件で検索できます。
またファイル名を正規表現で検索し、見つかったファイルに対してコマンドを実行することもできます。

高速に検索する必要がある場合、また対象ファイルが何処にあるかわからないときはlocateを使用します。
ファイル名以外の何らかの条件に紐付いた正確なファイル一覧が欲しい場合、取得したファイルに対して何かを行いたいときはfindを使用します。

Downloading Things

ping / wget / curl

pingはネットワークホストとの通信を確立しようとします。
主にインターネット接続がダウンしているかどうかを確認するために使用されます。

[ andrew@pc01 ~ ]$ ping google.com
PING google.com (74.125.193.100) 56(84) bytes of data.
Pinging 74.125.193.100 with 32 bytes of data:
Reply from 74.125.193.100: bytes=32 time<1ms TTL=64
...

wgetはインターネットからファイルを簡単にダウンロードできます。

[ andrew@pc01 ~ ]$ wget \
> http://releases.ubuntu.com/18.10/ubuntu-18.10-desktop-amd64.iso

curlwgetと同じように使用できます。
--outputオプションを忘れないようにしてください。

[ andrew@pc01 ~ ]$ curl \
> http://releases.ubuntu.com/18.10/ubuntu-18.10-desktop-amd64.iso \
> --output ubuntu.iso

curlwgetには、それぞれ長所と短所があります。
curlはより多くのプロトコルをサポートし、様々な場面で使用可能です。
curlはデータの送信もできますが、wgetは受信しかできません。
wgetはファイルを再帰的にダウンロードできますが、curlはできません。

一般的には、インターネットからファイルをダウンロードするときはwgetを使います。
curlを使ってデータを送信することはあまりありませんが、これを知っておくと、いざ必要になったときに役立ちます。

apt / gunzip / tar / gzip

Debian系のLinuxディストリビューションには、aptという素晴らしいパッケージ管理ツールが存在します。
ソフトウェアのインストール、アップデート、削除が可能です。
何らかのソフトウェアが必要になった場合、apt searchで検索し、apt installでインストールします。

[ andrew@pc01 ~ ]$ apt search bleachbit
...bleachbit/bionic,bionic 2.0-2 all
  delete unnecessary files from the system

# インストールにはsudoが必要
[ andrew@pc01 ~ ]$ sudo apt install bleachbit

Linuxのソフトウェアは大抵tarball(.tar.gz)になっています。

[ andrew@pc01 ~ ]$ wget \
> https://github.com/atom/atom/releases/download/v1.35.0-beta0/atom-amd64.tar.gz

この拡張子のファイルはgunzipで解凍できます。

[ andrew@pc01 ~ ]$ gunzip atom-amd64.tar.gz && ls
atom-amd64.tar

.tar.gzファイルをgunzipすると.tarファイルになり、さらにtar -xfで通常のファイルシステムに戻すことができます。

[ andrew@pc01 ~ ]$ tar -xf atom-amd64.tar && mv \
atom-beta-1.35.0-beta0-amd64 atom && ls
atom atom-amd64.tar

逆に圧縮するには、-cでディレクトリから.tarにまとめ、-zで圧縮します。

[ andrew@pc01 ~ ]$ tar -zcf compressed.tar.gz atom && ls
atom  atom-amd64.tar  compressed.tar.gz

gzipでもファイルを圧縮することができます。

[ andrew@pc01 ~ ]$ gzip atom-amd64.tar && ls
atom  atom-amd64.tar.gz compressed.tar.gz

Redirecting Input and Output

| / > / < / echo / printf

デフォルトでは、シェルコマンドは標準入力ストリーム(stdin、0)から入力を読み取り、標準出力ストリーム(stdout、1)に出力を書き込みます。
エラーが発生すると、それは標準エラーストリーム(stderr、2)に出力されます。

echoはデフォルトではstdoutに対してテキストを書き込みます。
ほとんどの環境では、それは単にターミナルに表示される、という意味です。

[ andrew@pc01 ~ ]$ echo "hello"
hello

パイプオペレータ|は、前のコマンドの出力を、後ろのコマンドの入力にリダイレクトします。

# wcはファイルの行数、単語数、バイト数を返すコマンド
[ andrew@pc01 ~ ]$ echo "example document" | wc
      1       2      17

>によるリダイレクトは出力先をstdoutから別のものに変更します。

[ andrew@pc01 ~ ]$ echo "test" > file && head file
test

printfechoの改良版で、フォーマットやエスケープシーケンスが書けます。

[ andrew@pc01 ~ ]$ printf "1\n3\n2"
1
3
2

<はstdinではなく、別のところから入力を取得します。

# sortは入力を行単位でソートするコマンド
[ andrew@pc01 ~ ]$ sort <(printf "1\n3\n2")
1
2
3

ファイルの中身をコマンドに送る推奨された方法は、UUOCではなく<を使うことです。
これを使った場合、データは自然に感じる左から右ではなく、右から左に流れることになるので注意してください。

[ andrew@pc01 ~ ]$ printf "1\n3\n2" > file && sort < file
1
2
3

0 / 1 / 2 / tee

012は、上記のようにそれぞれ標準入力、標準出力、標準エラーストリームを表します。
各ストリームは前述のように|<>各演算子を使ってリダイレクトできますが、それとは別に直接数値で書くこともできます。
その場合、標準出力は>&1、標準エラーは>&2となります。

[ andrew@pc01 ~ ]$ cat test
echo "stdout" >&1
echo "stderr" >&2

デフォルトでは標準出力も標準エラーもターミナルに出力されます。

[ andrew@pc01 ~ ]$ ./test
stderr
stdout

標準入力を/dev/nullに送ると、標準エラーだけがターミナルに表示されます。

[ andrew@pc01 ~ ]$ ./test 1>/dev/null
stderr

標準エラーを/dev/nullに送ると、標準出力だけがターミナルに表示されます。

[ andrew@pc01 ~ ]$ ./test 2>/dev/null
stdout

全てを/dev/nullに送る場合はこうです。

[ andrew@pc01 ~ ]$ ./test &>/dev/null

標準出力の出力先を変更するのではなく、複数の出力先に送りたい場合は、teeコマンドを使います。

[ andrew@pc01 ~ ]$ ls && echo "test" | tee file1 file2 file3 && ls
file0
test
file0  file1  file2  file3

Advanced

Superuser

sudo / su

自分の名前を知りたいときはwhoamiです。

[ andrew@pc01 abc ]$ whoami
andrew

コマンドを別のユーザとして実行したい場合は、sudo -u usernameというコマンドで実行できます。
ただしそのユーザのパスワードが必要です。

[ andrew@pc01 abc ]$ sudo -u test touch def && ls -l
total 0
-rw-r--r-- 1 test test 0 Jan 11 20:05 def

-uを省いた場合のデフォルトはスーパーユーザ、通常はrootであり、このユーザはあらゆる操作が無制限に可能です。

[ andrew@pc01 abc ]$ sudo touch ghi && ls -l
total 0
-rw-r--r-- 1 test test 0 Jan 11 20:05 def
-rw-r--r-- 1 root root 0 Jan 11 20:14 ghi

suで一時的に別のユーザになれます。
戻るときはexitです。

[ andrew@pc01 abc ]$ su test
Password:
test@pc01:/home/andrew/abc$ whoami
test
test@pc01:/home/andrew/abc$ exit
exit

[ andrew@pc01 abc ]$ whoami
andrew

sudosuの違いについてもっと知りたい人は、こちらの記事を読みましょう。

!!

スーパーユーザはソフトウェアのインストール、ユーザの作成などを行うことができる唯一のユーザです。
時々それを忘れてエラーを起こすことがあるでしょう。

[ andrew@pc01 ~ ]$ apt install ruby
E: Could not open lock file /var/lib/dpkg/lock-frontend - open (13: Permission denied)
E: Unable to acquire the dpkg frontend lock (/var/lib/dpkg/lock-frontend), are you root?

頭にsudoをつけてコマンドを再入力するとうまくいきます。

[ andrew@pc01 ~ ]$ sudo apt install ruby
Reading package lists...

!!で同じことができます。
これは前回入力したコマンドを保持しているショートカットです。

[ andrew@pc01 ~ ]$ apt install ruby
E: Could not open lock file /var/lib/dpkg/lock-frontend - open (13: Permission denied)
E: Unable to acquire the dpkg frontend lock (/var/lib/dpkg/lock-frontend), are you root?

[ andrew@pc01 ~ ]$ sudo !!
sudo apt install ruby
Reading package lists...

デフォルトでは、いちどsudoでコマンド実行に成功すると、その後15分間はパスワード入力なしにsudoコマンドを実行可能です。
15分を超えると、ふたたびパスワードを要求されるようになります。

File Permissions

File Permissions

ファイルに対して読み込み(r)、書き込み(w)、実行(x)の操作をそれぞれ可能だったりできなかったりします。
ファイルへのアクセス権はls -lコマンドで確認することができ、10文字で表されます

[ andrew@pc01 ~ ]$ ls -lh
total 8
drwxr-xr-x 4 andrew andrew 4.0K Jan  4 19:37 tast
-rwxr-xr-x 1 andrew andrew   40 Jan 11 16:16 test
-rw-r--r-- 1 andrew andrew    0 Jan 11 16:34 tist

各行の最初の文字は、ファイルの種類を表します。
d=ディレクトリ、l=シンボリックリンク、-=通常ファイルなど。
次の3文字はファイルの所有者(u)が持つパーミッション、次の3文字がファイル所有グループ(g)が持つパーミッション、最後の3文字が無関係なユーザ(o)の持つパーミッションです。

rはそのファイルを読み取り可能、wは書き込み可能、xは実行可能であるというパーミッションを表します。
ディレクトリが実行可能であるとは、そのディレクトリの内容を一覧表示できるという意味です。
この3権限のセットを1桁の数値で表すことがよくあります。
xが有効なら1、wが有効なら2、rが有効なら4というふうに2進数として計算します。
例としてr-xであればrxが有効なので5です。
従って、上記例の3ファイルは順に755、755、644のパーミッションを持つというふうに表されます。

パーミッションの次にある数値は、そのファイルを参照しているリンクの数です。

その次の2つの文字は、それぞれ所有者名と、所有者のグループ名を表します。
今回は所有者もグループも全てandrewです。

さらにその次がファイルのサイズ、更新日時、そして最後にファイル名が表示されます。

ls-hオプションは人間が読み取りやすいように出力を調整します。
すなわち4096などではなく4.0Kと表示します。

chmod / chown

chmodで数値を与えることにより、ファイルのパーミッションを変更できます。

[ andrew@pc01 ~ ]$ chmod 777 test && chmod 000 tist && ls -lh
total 8.0K
drwxr-xr-x 4 andrew andrew 4.0K Jan  4 19:37 tast
-rwxrwxrwx 1 andrew andrew   40 Jan 11 16:16 test
---------- 1 andrew andrew    0 Jan 11 16:34 tist

あるいはプラスマイナスとrwxで直感的操作もできます。

[ andrew@pc01 ~ ]$ chmod +rwx tist && chmod -w test && ls -lh
chmod: test: new permissions are r-xrwxrwx, not r-xr-xr-x
total 8.0K
drwxr-xr-x 4 andrew andrew 4.0K Jan  4 19:37 tast
-r-xrwxrwx 1 andrew andrew   40 Jan 11 16:16 test
-rwxr-xr-x 1 andrew andrew    0 Jan 11 16:34 tist

ファイルの所有者はchownで変更できます。

[ andrew@pc01 ~ ]$ sudo chown marina test

グループの変更はchgrpです。

[ andrew@pc01 ~ ]$ sudo chgrp hadoop tist && ls -lh
total 8.0K
drwxr-xr-x 4 andrew andrew 4.0K Jan  4 19:37 tast
-----w--w- 1 marina andrew   40 Jan 11 16:16 test
-rwxr-xr-x 1 andrew hadoop    0 Jan 11 16:34 tist

User and Group Management

Users

現在ログインしてる全ユーザの一覧がusersで表示できます。
複数のSSHセッションを張ったりなどで同一ユーザが同時に何度もログインすることができます。

[ andrew@pc01 ~ ]$ users
andrew colin colin colin colin colin krishna krishna

ログインしていないユーザも含め全ユーザを確認するには、/etc/passwdを参照します。
なお、このファイルを変更するとユーザアカウントが破損し、ログインできなくなる可能性があるため、絶対に直接編集しないでください。

[ andrew@pc01 ~ ]$ alias au="cut -d: -f1 /etc/passwd \
> | sort | uniq" && au
 _apt
anaid
andrew...

ユーザの追加はuseraddです。

[ andrew@pc01 ~ ]$ sudo useradd aardvark && au
_apt
aardvark
anaid...

ユーザの削除はuserdelです。

[ andrew@pc01 ~ ]$ sudo userdel aardvark && au
_apt
anaid
andrew...

ユーザアカウント操作の詳細についてはこちらを参照してください

Groups

groupsは、ユーザが属している全てのグループを表示します。

[ andrew@pc01 ~ ]$ groups
andrew adm cdrom sudo dip plugdev lpadmin sambashare hadoop

システム上の全てのグループを確認するには、/etc/groupを参照します。
このファイルを理解していないかぎり、直接変更はしないでください。

[ andrew@pc01 ~ ]$ alias ag=“cut -d: -f1 /etc/group \
> | sort&& ag
adm
anaid
andrew...

グループの追加はgroupaddです。

[ andrew@pc01 ~ ]$ sudo groupadd aardvark && ag
aardvark
adm
anaid...

グループの削除はgroupdelです。

[ andrew@pc01 ~ ]$ sudo groupdel aardvark && ag
adm
anaid
andrew...

グループ操作の詳細についてはこちらを参照してください

Text Processing

uniq / sort / diff / cmp

uniqコマンドは、連続した重複行を削除して表示します。

[ andrew@pc01 man ]$ printf "1\n2\n2" > a && \> printf "1\n3\n2" > b

[ andrew@pc01 man ]$ uniq a
1
2

sortは行をアルファベット順、もしくは数値順に並び替えます。

[ andrew@pc01 man ]$ sort b
1
2
3

diffは、2つのファイルでどの行が異なるかを表示します。

[ andrew@pc01 man ]$ diff a b
2c2
< 2
---
> 3

cmpは、2つのファイルで最初に異なる場所のバイト数を表示します。

[ andrew@pc01 man ]$ cmp a b
a b differ: char 3, line 2

cut / sed

cutは、文字列をなんらかの区切り文字で分割するコマンドで、CSV処理などに向いています。
-dで区切り文字を指定し、-fは出力するインデックス(1始まり)を指定します。

[ andrew@pc01 man ]$ printf "137.99.234.23" > c

[ andrew@pc01 man ]$ cut -d'.' c -f1
137

sedは、ファイル内の文字列を別の文字列に置換するためによく使われます。

[ andrew@pc01 man ]$ echo "old" | sed s/old/new/
new

実のところsedは非常に強力なユーティリティであり、この狭いスペースでは到底解説することができません。
sedはチューリング完全であり、他のプログラミング言語でできることなら何でもできます。
sedは、正規表現に基づいてテキストを検索・置換し、特定パターンに一致する、あるいは含む行を抽出したり、テキストファイルを非対話式にIn-place編集したり、その他様々なことが行えます。
詳しくは以下のようなチュートリアルを参照してください。
https://www.tutorialspoint.com/sed/
http://www.grymoire.com/Unix/Sed.html
https://www.computerhope.com/unix/used.htm

Pattern Matching

grep

grepの名前はsearch Globally for a Regular Expression and Print itに由来します。
ファイルから、特定のパターンに一致するテキストを見つけるために使用されます。

[ andrew@pc01 ~ ]$ grep -e ".*fi.*" /etc/profile
# /etc/profile: system-wide .profile file for the Bourne shell (sh(1))
    # The file bash.bashrc already sets the default PS1.
    fi
    fi
...

普通に単語検索もできます。

[ andrew@pc01 ~ ]$ grep "andrew" /etc/passwd
andrew:x:1000:1000:andrew,,,:/home/andrew:/bin/bash

ファイル内の一致する行を見つけた後、他のプログラムでその行を処理させたい場合などに、grepは最適な選択です。

grepでは、正規表現-E、複数文字列のいずれかとマッチさせる-F、ディレクトリ内のファイルを再帰的に検索する-rなどのオプションが存在します。
これらのオプションは元々個別コマンドegrepfgreprgrepとして実装されていましたが、個別コマンドは非推奨になりました。

おまけ
いろいろなコマンド名の由来を見てみよう。

awk

awkは、CSVなどのデリミタで区切られたデータファイルを読み取り、操作することを念頭に作られた言語です。
経験的に、grepはファイルから特定の文字列やパターンを見つけるのに適していて、sedはファイル内の文字列を1対1で置換するのに適しています。
そしてawkは、ファイルからパターンを抽出して分析するのに適しています。

awkができることの例として、2列のデータを含むファイルを処理してみます。

[ andrew@pc01 ~ ]$ printf "A 10\nB 20\nC 60" > file

行ごとにループし、数値を合計し、平均を出力するコードは以下のようになります。

[ andrew@pc01 ~ ]$ awk 'BEGIN {sum=0; count=0; OFS=" "} {sum+=$2; count++} END {print "Average:", sum/count}' file
Average: 30

sedawkはどちらもチューリング完全な言語です。
これらはパターンマッチングとテキスト処理に非常に役立つ言語で、そしてそれぞれについて書かれた本もたくさんあります。
それらについて書き記すにはここには余白が足りません。
もっと調べてみてください。

おまけ
sedgrepawkの違いについて学ぼう

Copying Files Over ssh

ssh / scp

sshは、Unixベースのマシン同士がネットワークを介して接続する方法です。

[ andrew@pc01 ~ ]$ ssh –p <port> andrew@137.xxx.xxx.89
Last login: Fri Jan 11 12:30:52 2019 from 137.xxx.xxx.199

別のマシンに入ったのでプロンプトが変わりました。

[ andrew@pc02 ~ ]$ exit
logout
Connection to 137.xxx.xxx.89 closed.

マシン1でファイルを作成します。

[ andrew@pc01 ~ ]$ echo "hello" > hello

これをマシン2にコピーするにはscpコマンドを使用します。
sshはポート番号を-pで指定するのに対し、scp-Pで指定するのに注意しましょう。

[ andrew@pc01 ~ ]$ scp –P <port> hello andrew@137.xxx.xxx.89:~
hello                                         100%    0     0.0KB/s   00:00

確認のため、sshでマシン2に入りましょう。

[ andrew@pc02 ~ ]$ ssh –p <port> andrew@137.xxx.xxx.89
Last login: Fri Jan 11 22:47:37 2019 from 137.xxx.xxx.79

そこにファイルが存在しているはずです。

[ andrew@pc02 ~ ]$ ls
hello  multi  xargs

[ andrew@pc02 ~ ]$ cat hello
hello

rsync

rsyncは、ファイル間の差分を調べることにより、コピーされるデータ量を少なく抑えるファイルコピーツールです。

2つのディレクトリdsが存在し、dにはファイルがひとつ、sにはふたつ存在するとします。

[ andrew@pc01 d ]$ ls && ls ../s
f0
f0  f1

ディレクトリをrsyncで同期すると、不足しているファイルだけがコピーされます。

[ andrew@pc01 d ]$ rsync -av ../s/* .
sending incremental file list...

最終的にdディレクトリには、sディレクトリに存在する全てのファイルが集まります。

[ andrew@pc01 d ]$ ls
f0  f1

rsyncssh経由でも実行できます。

[ andrew@pc02 r ]$ ls

[ andrew@pc02 r ]$ rsync -avz -e "ssh -p <port>" andrew@137.xxx.xxx.79:~/s/* .
receiving incremental file list
f0
f1

sent 62 bytes  received 150 bytes  141.33 bytes/sec
total size is 0  speedup is 0.00

Long-Running Processes

yes / nohup / ps / kill

ネットワークやハードウェアの問題によって、SSH接続が切断されることがよくあります。
その際、そのSSH接続から起動されていたプロセスには全てHUPシグナルが送られ、プロセスは強制終了されます。

yesコマンドは、停止されるまで"y"を出力し続けます。
これをnohupと一緒に実行しましょう。

[ andrew@pc01 ~ ]$ nohup yes &
[1] 13173

psは、現在のユーザが実行しているプロセスを一覧表示します。
以下の例では、yesコマンドのPIDは13713です。

[ andrew@pc01 ~ ]$ ps | sed -n '/yes/p'
13173 pts/10   00:00:12 yes

ログアウトして再度ログインしてみましょう。

[ andrew@pc01 ~ ]$ ps | sed -n '/yes/p'

表示されなくなりました。
が、実際は裏で実行され続けていてtophtopには出てきます。

[ andrew@pc01 ~ ]$ top -bn 1 | sed -n '/yes/p'
13173 andrew    20   0    4372    704    636 D  25.0  0.0   0:35.99 yes

killコマンドに-9オプションを付けて、このプロセスを強制終了しましょう。

[ andrew@pc01 ~ ]$ kill -9 13173

これでプロセスが終了したため、もうtopにも出てきません。

[ andrew@pc01 ~ ]$ top -bn 1 | sed -n '/yes/p'

cron / crontab / >>

cronはタスクを定期的に実行する簡単な方法を提供します。

crontab –eコマンドでテキストエディタが開き、cronを編集することができます。
以下の行を追加しましょう。

* * * * * date >> ~/datefile.txt

毎分dateコマンドを実行し、出力をファイルに保存します。

[ andrew@pc02 ~ ]$ head ~/datefile.txt
Sat Jan 12 14:37:01 GMT 2019
Sat Jan 12 14:38:01 GMT 2019
Sat Jan 12 14:39:01 GMT 2019...

再びcrontabを編集し、該当行を削除すると、このジョブは停止します。
ジョブの頭にある* * * * *は順に分(0-59)、時間(0-23)、日(1-31)、月(1-12)、曜日(0-6もしくはSun-Sat)を表します。
*を数値に置き換えることで、特定の日や特定の時間にのみジョブを実行することができるようになります。

曜日に関係なくジョブを実行したい場合は、5番目の**のままにしておきます。
全ての項目を*にすると毎分実行されるようになり、これが利用可能な最小間隔になります。
システムが再起動されたときだけ実行するように設定することもでき、これは* * * * *のかわりに@rebootと記載します。
また、1時間あるいは一日のうち特定の回数だけ実行するようにしたり、月や曜日や日など複数項目の組み合わせで指定することも可能です。

もっと詳しく知るにはこちらを参照してください

Miscellaneous

pushd / popd

ディレクトリの移動をスタックするには、cdではなくpushdpopdを使います。
以下の例ではホームディレクトリから開始します。

[ andrew@pc01 ~ ]$ pwd
/home/andrew

フルパスのpushdでディレクトリを移動します。

[ andrew@pc01 ~ ]$ pushd /etc/java/security/security.d/
/etc/java/security/security.d ~

pushdでサブディレクトリに移動します。

[ andrew@pc01 security.d ]$ pushd ~/test/
~/test /etc/java/security/security.d ~

pushdによって積まれたスタックは、表示の一番左側に追加されます。
popdすると一番最後に追加されたディレクトリに戻ります。

[ andrew@pc01 test ]$ popd
/etc/java/security/security.d ~

[ andrew@pc01 security.d ]$ pwd
/etc/java/security/security.d

何度もpopdすると、最終的にpushdを始めたディレクトリに戻ります。

[ andrew@pc01 security.d ]$ popd
~

xdg-open

xdg-openは、ファイルをデフォルトのアプリケーションで開くコマンドです。
HTMLファイルをブラウザで開きたい場合などに便利です。
MacOSで言うopenコマンドに相当します。

[ andrew@pc01 security.d ]$ xdg-open index.html

01.png

xargs

xargs引数のリストに対して、ループ的にコマンドを実行します。

以下はカレントディレクトリ、親ディレクトリ、親の親ディレクトリに対してlsを実行する例です。

[ andrew@pc01 ~ ]$ export lv=".\n..\n../.."

[ andrew@pc01 ~ ]$ printf $lv | xargs ls
.:
multi  file

..:
anaid  andrew  colin...

../..:
bin    dev   index...

引数は、–Iオプションを使うことで次のコマンドの引数に割り当てることができます。

以下はカレントディレクトリ、親ディレクトリ、親の親ディレクトリに対してcdpwdを実行する例です。

[ andrew@pc01 ~ ]$ printf $lv | xargs -I % sh -c 'cd %; pwd %'
/home/andrew
/home
/

もっと詳しく知るには、この素晴らしいチュートリアルをご覧ください。

Bonus: Fun-But-Mostly-Useless Things

楽しいけど役に立たないもの。

w / write / wall / lynx

wは誰がログインしていてさらに何をしているかを表示する、whoの詳細版です。

[ andrew@pc01 ~ ]$ w
 17:32:42 up 434 days,  3:11,  8 users,  load average: 2.32, 2.46, 2.57
USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT
colin    pts/9    137.xx.xx.210    03Jan19  5:28m  1:12   0.00s sshd: colin [priv]
andrew   pts/10   137.xx.xx.199    11:05    1.00s  0.15s  0.04s sshd: andrew [priv]
colin    pts/12   137.xx.xx.210    03Jan19 34:32   1.59s  1.59s –bash
...

別のユーザに対してwriteでメッセージを送信することができます。

[ andrew@pc01 ~ ]$ echo "hello" | write andrew pts/10

Message from andrew@pc01 on pts/10 at 17:34 ...
hello
EOF

wallwriteと似ていますが、ログイン中の全ユーザに同じメッセージを送信します。
メールやTwitterやWhatsAppといったアプリが普及する前は、メッセージを送るためにwritewallがよく使われていました。

lynxはテキストベースの、完全に機能するWebブラウザです。

02.png

nautilus / date / cal / bc

nautilusは新しいセッションを開き、GUIのファイルマネージャをオープンします。

dateは現在の日付と時刻を表示します。

[ andrew@pc01 ~ ]$ date
Fri Jan 11 17:40:30 GMT 2019

calは今月のカレンダーをテキストで表示します。
本日の日付は強調表示されます。

[ andrew@pc01 ~ ]$ cal
    January 2019
Su Mo Tu We Th Fr Sa
       1  2  3  4  5
 6  7  8  9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31

bcは簡易的な電卓です。
かわりにPythonを使いましょう。

[ andrew@pc01 ~ ]$ bc
bc 1.06.95 ...
20/4
5

これでおしまいです!
このリストに追加する必要のある機能や、クールなコマンドを知っていたら教えてください。
またタイプミスや間違いがあったときも教えてください。
ベストを尽くしたつもりですが、きっとたくさんあると思います。

この投稿が参考になったのであれば、ぜひコーヒーを買ってください

Update 8 July 2019

2年前に書かれた、とてもよく似た記事を発見しました
フランス語がわかるのであれば、私の記事の補完に最適だと思います。

コメント欄

「素晴らしいポスト!Andrewサンクス!」
「同じ投稿をPowerShellでやってくれたら思い残すことはない。」
screen。新しいSSH接続を開く。そのセッションはログオフしてもプログラムはずっと実行されたままで、さらに再接続して続きもできる。」「tmuxはいいぞ。screenと似てるけどもっと便利。」
disown。既に動かした後のプログラムを、後からバックグラウンドに持っていける。」
ipythontrash-cliを是非。」
date +%sでUNIXタイムを表示できるよ。」
ncdu初めて知った。これ便利!」
「MacとFreeBSDではtopのユーザ名オプションは-Uだったよ。」
kill -9はいかん、kill -15か単にkillってするんだ。-15はOSに通知が行く、-9はいきなり電源押して切るようなものだ。」
sourceshは同じ結果になることもあるけど全く同じではない。sourceは現在のbash上で実行されてexportした変数も使える。shは新たなシェルを起動するしexportした変数は使えない。」
bashコマンドって言うけど実際はbashコマンドではなくてUnix/Posixコマンドが多いぞ。」
xdg-openの画像がWindowsっぽいんだけどどゆこと?」「MobaXTermを使ってWindowsからUbuntuにログインしてるからなのだ。」
「コマンドプロンプトに|を使っててパイプとわかりにくいのでプロンプトは$にしてほしい。」「ありがとう更新したよ!」
「聖なる牛!」

感想

チュートリアル形式でわかりやすい、bashコマンド入門記事です。
最後まで読めばbashスクリプトをバリバリ組めるようなエキスパートになれるかというと怪しいですが、汎用的なbashコマンドについてはひととおり使えるようになっていることでしょう。

LPICのLevel3まで持ってたらよゆーよゆーとか思ってたけど、わりと知らないコマンドも多かった。
知っていると知らないとでは大違いなので、コマンドや機能が存在していることそのものを認識しておくと、今後役に立つことでしょう。
いきなり詳しく知る必要はありません。
詳細は必要になったときにmanなりググるなりすればいいんですよ。

あと原文は、こうすればこうなりますよというのは書かれているんだけど、"何故"そうなるかが書かれていないところがあってちょっと躓く感じがありました。
和訳にあたりそのあたりを一部補完しています。

トップコメのHoly cow!はどう捉えたらいいのかよくわからなかった。


多重Dictionaryをつくりたかった

$
0
0

C#は素人なので定石がわかりません。

こういう連想配列があったとしましょう。

PHP
$datetimes = [
    'past' => [
        'yesterday' => new DateTime('yesterday'),
        'lastweek' => new DateTime('last week'),
    ],
    'future' => [
        'tomorrow' => new DateTime('tomorrow'),
        'nextyear' => new DateTime('next year'),
    ]
];

いや、こんな意味のわからない配列なんて作らねえよという抗議は全くもってその通りなのですがそこはスルーします。
実際はAPIに飛んできたリクエストをユーザ単位にまとめるみたいな処理をするところで、何個飛んでくるかわからないという仕様です。

PHPで書くとこんな感じでさくっとできます。

PHP
$datetimes = [];
foreach($request as $v){
    $datetimes[$v['tense']][$v['key']] = new DateTime($v['value']);
}

一瞬で書けてとってもらくちん。

ということで、この連想配列をC#に移植しようとしたのですが、どうもこのような構造について話をしているところがほとんど見当たりませんでした。
多重連想配列はC#では禁忌なのだろうか。

連想配列はDictionaryを使えばいいみたいですが、これは基本的に値がひとつです。
NameValueCollectionというのが目的に近かったのですが、これは値がstring固定みたいです。なぜ。
Microsoft.Experimental.Collectionsは名前が既に危ない。

試しにDictionaryの中にDictionaryを突っ込んでみたら普通にいけました。

    var datetimes = new Dictionary<string, Dictionary<string, DateTime>>();

    var list1 = new Dictionary<string, DateTime>();
    list1.Add("yesterday", new DateTime("yesterday"));
    list1.Add("lastweek", new DateTime("last week"));

    var list2 = new Dictionary<string, DateTime>();
    list2.Add("tomorrow", new DateTime("tomorrow"));
    list2.Add("nextyear", new DateTime("next year"));

    datetimes.Add("past", list1);
    datetimes.Add("future", list2);

ただ、この構造だとlist1などの中間変数が必要になってしまいます。
一気にdatetimes["past"].Add("yesterday", new DateTime("yesterday"));みたいなことはできないの?

    var datetimes = new Dictionary<string, Dictionary<string, DateTime>>();

    // The given key "past" was not present in the dictionary
    datetimes["past"].Add("yesterday", new DateTime("yesterday"));

    // Object reference not set to an instance of an object
    datetimes.Add("past", null);

    // An item with the same key has already been added
    datetimes.Add("past", new Dictionary<string, DateTime>{{"yesterday", new DateTime("yesterday")}} );
    datetimes.Add("past", new Dictionary<string, DateTime>{{"lastweek", new DateTime("last week")}} );

    // これはOK
    datetimes.Add("past", new Dictionary<string, DateTime>{{"yesterday", new DateTime("yesterday")}} );
    datetimes["past"].Add("lastweek", new DateTime("last week"));

Add自体はできるみたいですが、親Dictionaryのキーの有無によって動作を変えなければなりません。
キーが存在しない場合のみキーをAddし、その後はその中身だけAddしないといけないようです。
なんというかとても面倒。

もっとなんかマシな方法ってのがあるんだろうけどよくわかりませんでした。
そもそもC#ではどういう作りにするのが定石なんだろうか。

わかりません><教えてください><

$
0
0

シャミロスでつらい。

2期が来るまで現実を忘れるためにkey関数のソースを掘ってみようと思ったのだ。

これがkey関数のソースです。

PHP_FUNCTION(key)
{
    HashTable *array;

    ZEND_PARSE_PARAMETERS_START(1, 1)
        Z_PARAM_ARRAY_OR_OBJECT_HT(array)
    ZEND_PARSE_PARAMETERS_END();

    zend_hash_get_current_key_zval(array, return_value);
}

なんだ、凄く短いですね。
これなら楽勝じゃないですか。

しかし幾つかマクロが使われているので、ちょっと中身を調べてみましょう。

ZEND_PARSE_PARAMETERS_STARTの実装

#define ZEND_PARSE_PARAMETERS_START(min_num_args, max_num_args) \
    ZEND_PARSE_PARAMETERS_START_EX(0, min_num_args, max_num_args)

1行のたらい回し。
ZEND_PARSE_PARAMETERS_START_EXの実装はこんな。

#define ZEND_PARSE_PARAMETERS_START_EX(flags, min_num_args, max_num_args) do { \
        const int _flags = (flags); \
        int _min_num_args = (min_num_args); \
        int _max_num_args = (max_num_args); \
        int _num_args = EX_NUM_ARGS(); \
        int _i; \
        zval *_real_arg, *_arg = NULL; \
        zend_expected_type _expected_type = Z_EXPECTED_LONG; \
        char *_error = NULL; \
        zend_bool _dummy; \
        zend_bool _optional = 0; \
        int error_code = ZPP_ERROR_OK; \
        ((void)_i); \
        ((void)_real_arg); \
        ((void)_arg); \
        ((void)_expected_type); \
        ((void)_error); \
        ((void)_dummy); \
        ((void)_optional); \
        \
        do { \
            if (UNEXPECTED(_num_args < _min_num_args) || \
                (UNEXPECTED(_num_args > _max_num_args) && \
                 EXPECTED(_max_num_args >= 0))) { \
                if (!(_flags & ZEND_PARSE_PARAMS_QUIET)) { \
                    if (_flags & ZEND_PARSE_PARAMS_THROW) { \
                        zend_wrong_parameters_count_exception(_min_num_args, _max_num_args); \
                    } else { \
                        zend_wrong_parameters_count_error(_min_num_args, _max_num_args); \
                    } \
                } \
                error_code = ZPP_ERROR_FAILURE; \
                break; \
            } \
            _i = 0; \
            _real_arg = ZEND_CALL_ARG(execute_data, 0);

なんだこれ。

尤も、このあたりは定型文なので、毎回わざわざ掘り返す必要はありません。
ZEND_PARSE_PARAMETERS_STARTとZEND_PARSE_PARAMETERS_ENDで挟まれたところにZ_PARAM_XXXと書いておけば引数を受け取れるというイメージでよいです。

    ZEND_PARSE_PARAMETERS_START(1, 1)
        Z_PARAM_ARRAY_OR_OBJECT_HT(array)
    ZEND_PARSE_PARAMETERS_END();

key関数は配列もしくはオブジェクト型の引数をひとつ受け取ります。
はい、マニュアルには引数は配列って書いてあるのですが、実際は何の問題もなくオブジェクトを渡せます
まあ今回はそこが焦点ではないのでどうでもいいとして、要するにこの3行は、単に引数を受け取る処理です。

ということでこの関数は、実質zend_hash_get_current_key_zval(array, return_value);の1行だけということになります。

zend_hash_get_current_key_zvalは小文字だからわかりにくいですが、これも実はマクロです。
zend_hash_get_current_key_zvalの実装

#define zend_hash_get_current_key_zval(ht, key) \
    zend_hash_get_current_key_zval_ex(ht, key, &(ht)->nInternalPointer)

PHPには1行だけのマクロが大量にあるのですが、これは何の意味があるのだろう。
zend_hash_get_current_key_zvalはkey関数しか使ってないんだけど、これ直接zend_hash_get_current_key_zval_exを呼ぶようにしたらいけないんですかね?(わかってない)

zend_hash_get_current_key_zval_exでようやく処理部分に到達しました。

ZEND_API void ZEND_FASTCALL zend_hash_get_current_key_zval_ex(const HashTable *ht, zval *key, HashPosition *pos)
{
    uint32_t idx;
    Bucket *p;

    IS_CONSISTENT(ht);
    idx = _zend_hash_get_valid_pos(ht, *pos);
    if (idx >= ht->nNumUsed) {
        ZVAL_NULL(key);
    } else {
        p = ht->arData + idx;
        if (p->key) {
            ZVAL_STR_COPY(key, p->key);
        } else {
            ZVAL_LONG(key, p->h);
        }
    }
}

ここまで来れば詳細を読まなくても、_zend_hash_get_valid_posで現在地を取ってきて、範囲外ならNULLを返す、キーが文字列ならstring値を、数値ならlong値を返す、ってだいたいわかりますね。

しかし脈絡なく出てくるIS_CONSISTENTってなんだ?

これはZEND_DEBUGであれば_zend_is_inconsistent(a, __FILE__, __LINE__);で、そうでなければ空っぽになるようです。
ZEND_DEBUGはコンパイルオプション--enable-debugを指定してコンパイルしたときに有効になる値です。
つまり完全にデバッグ用途みたいなので今回はスルーしましょう。

さて見て見ぬふりをしましたが、ZEND_FASTCALLやらZVAL_STR_COPYやらZVAL_LONGやらも全部マクロです。
その中でもさらにマクロが使われていてさらにその中でも…の無限連鎖で、どこまで追っても終わりません。

関数ひとつ見るだけで力尽きました。
こんなのをさくさく書いてる人たちはいったいどうやってPHPを理解してるのですか?
わかりません><教えてください><

【Laravel5.8】二つの配列パラメータ数が同じバリデーションがほしい

$
0
0

二つの配列パラメータ数が同じバリデーションを追加したい。
一カ所だけでしか使わないからフォームリクエストを使うほどでもない。

どういうことかというとこんなかんじ。

// OK
test.php?name[]=100&value[]=980
test.php?name[]=100&value[]=980&name[]=200&value[]=2000
test.php?name[1]=100&value[1]=980

// NG
test.php?name[]=100&value[]=980&name[]=200
test.php?name[]=100&value[]=980&value[]=2000
test.php?name[1]=100&value[2]=980

たとえば商品IDと個数、みたいなセットで送る必要のあるパラメータに使いたい。
その場合はitems[商品ID]=個数とかのほうがいいだろうけど、3点以上のパラメータが必要だとか、色々とそうもいかないことがあるわけですよ。

コントローラ
    // バリデータ
    $validation = [
        'name' => 'required|array',
        'name.*' => 'required|integer',
        'value' => 'required|array',
        'value.*' => 'required|integer',
    ];
    $v = Validator::make($request->all(), $validation);

    // パラメータ数が同じバリデーション
    $v->after(function ($validator) {
        $data = $validator->getData();
        if (!isset($data['name']) || !isset($data['value'])
            || !is_array($data['name']) || !is_array($data['value'])
            || array_keys($data['name']) !== array_keys($data['value'])
        ) {
            $validator->errors()->add('name', 'nameとvalueの個数は同じでないといけない');
        }
    });

    // バリデーション実行
    if ($v->fails()) {
        return 'えらー';
    }

なんだこれ。
目的は達成しているのですがコードがとても微妙。

namename.*などのバリデーションに成功しても失敗しても必ずafterは呼ばれます。
すなわち当初の$validationと同等の事前チェックをafterにも再度書かないといけません。

だからといってチェック後の値を取得しようと$validator->getData()$validator->validated()にしたら無限ループで死にます。
あと試しに$validator->fails()ってしてみたらApacheごと死んだ。何故。

このあたりもうちょっとうまい書き方はないものだろうか。
おそらくあるんだろうけどよくわかりませんでした。

【PHP8.0】オブジェクト初期化子のRFCが却下されそう

$
0
0

いつのまにやらObject InitializerというRFCが投票に入っていました。
ちょっとだけ面白そうと思ったのですが、ただ、ほぼ確実に却下されるので詳しく見てもしょうがないのでざっくり紹介してみます。

Object Initializer

文法

class Customer{
  public $id;
  public $name;
  private DateTimeImmutable $createdAt;
}

$customer = new Customer{
  id = 123,
  name = "John Doe",
};

newするときに中括弧で引数を渡すと、自動的にプロパティにセットされます。

キーが文字列ではないところが、PHPとして物凄い違和感がありますね。

上の例は、下のようなよくある文と同等です。

$customer = new Customer();
$customer->id = 123;
$customer->name = "John Doe";

従って、privateである$createdAtに値を突っ込むことはできません。

制約

オブジェクト初期化子を使う場合、全てのpublicプロパティを指定しなければなりません。

$customer = new Customer{
  id = 123, // RuntimeException class object failed due to missing required properties
};

オブジェクト初期化子自体を使わない場合は、普通にインスタンス化できます。

$customer = new Customer();

未定義プロパティ

未定義のプロパティに値を突っ込めます。

$baz = 'baz';

$obj = new stdClass {
  foo = "bar",
  $baz = true,
};

ええー、と思いますが、そもそもこれPHPの仕様だったわ。

コンストラクタ

オブジェクト初期化子を使う場合、コンストラクタに引数は渡せません。
即ち、以下のような書き方は文法エラーになります。

$customer = new Customer($dateTime){
  id = 123,
  name = "John Doe",
};

マジックメソッド

可視プロパティがなかった場合、普通にマジックメソッド__setが呼ばれます。
RFCに書かれている以下の例では、$nameはpublicなので直接値が入り、protectedである$emailはマジックメソッド__setが呼ばれることになるようです。

class EmailAddress
{
  protected string $email;
  public ?string $name = null;

  public function __set(string $name, $value): void
  {
    if ($name !== "email") {
      return;
    }
    if (!filter_var($value, FILTER_VALIDATE_EMAIL)) {
      throw new InvalidArgumentException("Invalid email address");
    }
    $this->email = $value;
  }
}

$email = new EmailAddress {
  email = "john.doe@example.org",
  name = "John Doe",
};

将来の予定

無名クラスstdClassを使うときは、もうクラス名すらも書かなくてよくない?

$obj = new {
  foo = "bar",
  $baz = true,
};

配列展開してpublicプロパティ突っ込めたらよくない?

$array = [
  'a' => 1,
  'b' => 2,
];

$obj = new { ...$array };

投票

2019/10/07投票開始、2019/10/21投票終了、可決には2/3+1の賛成が必要です。
2019/10/09現在は賛成3反対18で、ほぼ確実に却下されます。

この投票期間、RFCには何故か書かれていません。

感想

うん、まあ却下だよね。

特にpublicプロパティは全て指定しなければならないという厳格さと、未定義プロパティを指定できるという緩さが同居してるのは気持ち悪いというかなんというか。
これならまだAutomatic Property Initializationのほうがいいと思います。
Code free constructorよりはいいと思いますが。

ただ無名クラスはすごい便利そうなので、これは欲しいところですね。

$obj = new{
  'id' = 123,
  'name' = 'John Due',
};

まあ、今でもほぼ同じように書けたりはするんですけどね。

$obj = (object)[
  'id' => 123,
  'name' => 'John Due',
];

GitHubのスターは金にならない

$
0
0

Sizzyというツールがあります。
様々なサイズの画面を同時に表示し、レスポンシブレイアウトがどうなるかを一気に確認できるという便利なツールです。
スマホ向けWebサイトを開発している人なら、持っていると非常に便利でしょう。

参考:レスポンシブの確認ツール、後発だけに便利!スマホ・タブレットの主要なデバイスでの表示確認が同時にできる -Sizzy / 『Sizzy』様々なデバイスでのサイト表示を確認出来る便利サイト

さてこのSizzy、元々無償のChromeエクステンションだったのですが、先日2019年7月に単独のアプリとして有償化されました。
一人あたり月5ドル(団体割引あり)のサブスクリプション制です。

以下は作者のKizte( TwitterMediumWebサイト )がその理由や経緯を綴った記事、GitHub stars won't pay your rentの日本語訳です。
実体験だけあって切実で楽しい(楽しくない)、実に読ませる内容でした。

GitHub stars won’t pay your rent

こんにちは 👋

ここで前回の記事を書いてからずいぶん経ちましたが、単に生存証明だけのためにどうでもいい記事をねじ込みたくなかったというだけのことです。

しかし、今の私には伝えるべき物語があります。
先月(2019年7月)、Sizzyの新たなバージョンをリリースしました。
元々はシンプルなWebアプリだったものが、デザイナーや開発者向けの本格的なブラウザアプリに生まれ変わりました。
リリース以来のこの一ヶ月はとてもエキサイティングだったのですが、実際はリリースに至るまでの2年半にわたっても浮沈を経験してきました。
私は多くの間違いを犯し、多くの教訓を学んだため、その記録をみんなと共有したいと思います。

Solve your own problem, everything else will follow

これまで取り組んできた全ての仕事において、私は自分自身の問題を解決しようとしていました。
当時私はフリーランサーとして働いていましたが、作ったスマホ向けWebアプリを複数のデバイスでプレビューすることが大変な苦痛でした。
クライアントは、アプリがあらゆるデバイスのあらゆる向きで完全に動作することを求めていました。
そのため、アプリを更新するたびにChromeのデバイス設定20種類を切り替えて全ての動作確認を行うというクレイジーな作業が必要でした。
そんなときこそ自動化です。
自動化を行うプロダクトを数時間かけて作り、その後は一回に3秒かかるタスクを何度も何度も繰り返す必要はなくなりました。

同じころ、妹は離れた2地点でひとつのバッテリーを使い回すために一日10回バッテリーを抜き差しする作業を一年間続け、二つ目のバッテリーを購入するという解決策を採らなくても全く平気な顔をしていました。
人によって忍耐度は違うのでしょう。

作ったアプリを初公開したときのオリジナルの記事はこちらで読むことができます。

話を戻しましょう。
翌日早々と、私は多くの時間を節約することに成功しました。
アプリをオープンソースでGitHubに公開したため、誰もがそれを使用し、コントリビュートすることができました。
みんながこのアプリを気に入り、プロジェクトは5000のスターをゲットし、アクセス解析は狂ったような値を出力しました。
ちょっと何が起こっているのか信じられませんでした。

01.jpeg

Hopefully, people will donate, right?

私はOpen Collectiveを始め、2年半でなんと93ドルもの金額をゲットすることに成功しました。
このことをツイートしたとき、幾人かの反応は『たぶんみんなアプリを使ってないだけ』でした。
彼らが私を追い落とそうとしてるだけなのは知っていましたが、分析は別の結論を出していました。
毎月7千~1万人ほどがこのアプリを使っていたのです。
寄付のリンクは常に表示していたので、使用者がそれを見逃していたということはないでしょう。

ひとつ教訓を得ました。
何かを無料で配ったとき、それに対してわざわざ金を払おうとする人はいません。
これは人間として自然な考え方であり、目新しいものでもありません。
何らかのアプリを使っていて出てきた『希望金額を寄付する』のスライダーを1ドル以上に指定したのはいつですか?

え、一度でもやったことがある?
はい、あなたは酔っ払ってうっかり2.5ドルを選択してしまった聖人です。

I made a huge mistake

SizzyをWebアプリとして提供することが大きな間違いであったことに気付くまで、さほど時間はかかりませんでした。
やりたいことを全く実現しきれない、多くの制限がWebアプリにはありました。
私は多くのアイデアを持っていましたが、それらのほとんどはWebアプリで実現することはできないものでした。
本当のブラウザを作りたかったのです。
従って、私はElectronに手を出しました。
Electronの最初のバージョン0.0.0.0.0.1が動くようになったころ、みんなが同じ経験をしないで済むように、ReactとElectronを連携させるために苦労した記事を公開しました
私は前に進んでいました。

誰も全く寄付なんてしなかったので、私はこのアプリの有料版を作ることにしました。
しかし、どのようなアプローチを取るべきかわかりませんでした。

・オープンソース版を削除するのは、いかにも金儲けのための器の小さいムーブに見えてしまう
・オープンソース版と有料版を両方提供していくのはとても大変そう

私はGregに連絡し、彼のプロダクトInsomniaはどうしているのか尋ね、そして彼は親身に助けてくれました。
しかし、目標が遠すぎて、そこまで到達するための明確な道筋を思いつかなかったとき、我々は常に同じ、世界で最も有名な引き出しに手を伸ばします。

02.png

私はマケドニアで生まれ、当時オランダに住んでいましたが、両国ともにStripeにアクセスすることができませんでした。
Stripeに尋ねてみると、すぐにオランダでもアクセスできるようにすると言われました(そして実際数ヶ月後にロンチされた)が、私は既に課金なんてできないよという言い訳に囚われていたので、それを実際に使うことはありませんでした。

つまり、全てのクールな開発者がStripeを使っていますよね。
彼らは派手でクールなWebサイトと誰もが気に入るダッシュボードを持っていますよね。
私は自分のアプリを、丸い角とくっきりした影を持つ可愛くてカラフルなダッシュボードに仕立てなければなりません。
お金を出して買えるアプリが私のアプリ以外に108351もあるのです。
こんな競合の中で売れる?無理でしょ。
アプリを自動更新する方法を考えないといけません。
インストーラをどこに置いておく?
多額の費用がかかる場合、どうやって工面する?
これらの混乱をどうやって鎮める?
ライセンス、登録、メール、ニュースレター、その他諸々、やらなければならないことは山積みです。

私はこれらの問題を全て忘れることにし、そして2年が経過しました。

Shifting focus

Sizzyをリリースしたあとでフリーランスを辞め、React Academyを立ち上げたことで、そちらの仕事が手一杯になり、Sizzyは全く使わなくなっていました。
アプリのissueなどへの対応も辞め、完全に放置していました。
しかしアプリの使用者はまだまだ多く、むしろさらに増加していました。
基本的な機能しかなかったとしても、このアプリは多くの開発者にとって有用だったのです。
このアプリの権利を購入したいという開発者からいくつかのオファーを受けましたが、私はこれを売りたくはありませんでした。
私は、いつの日かまたSizzyが必要になるだろうことを知っていました。

If it doesn’t work for me, I don’t want to sell it

昨年の夏、ひさしぶりにアプリを使ってみましたが、そのときの感想は『どうしてみんなこんなものを使っているんだ?もっと良くできるだろう』でした。
しかし、皆がそう考えているわけではありません。
考え方を転換してみることで全ての問題が見えてきます。
頭の中にこのアプリの次のバージョンが既に存在するからこそ、現状のアプリが気に入らなかったのです。
従って、私は遂に再びこのアプリに取り組みたいと決意しました。
このアプリを、毎日使う道具のようにごく自然に起動するものにする、それが目標でした。

A second chance

私はワークショップやカンファレンスで忙しかったため、そのうえ他のプロジェクトにまで集中して作業する時間がほとんど取れませんでした。
また、私は毎週旅行していたので、次のような言い訳を思いつきました。

長期的に一カ所に留まっていないかぎり、集中して適切な仕事を行うように自らのコンディションを整えることはできない。 - Kitze

しかし、私はここでSizzyを諦めたくなかったので、Praneetを雇って一日数時間アプリのために働いてもらうことにしました。
彼は技術的観点からSizzyをモダナイズしてくれました。
多数あった古い依存を整理し、幾つかのライブラリを変更し、コードをクリーンにしてくれました。

真の問題は、プロジェクトの目標を明確に定めていなかったことです。
やり残したことはたくさんあり、どこから始めればいいのかわかりませんでした。
課金処理はまだ組み込まれておらず、ランディングページもありませんでした。
彼がバグ修正や新機能の追加に取り組んでいたころ、ようやくランディングページを作り始めました。
新機能のほとんどは、私が深く考えないまま進めていたので、一度の実装で完成することはありませんでした。
私たちは1歩進んで2歩下がりました。
時間を完全に無駄にしたとまではいいませんが、物事の優先順位付けと見積りをうまく設計していくべきでした。
残念ながら、私はそうではありませんでした。
一週間ごとの目標も最終目標期日も設定せず、ただ単にコーディングしていました。

もうひとつの問題は、私が集中してまっすぐ目的まで作業できないことでした。
私はランディングページの作成を開始しましたが、いつの間にかランディングページを作成するためのReactライブラリを作成していました🤦

これらの問題を解決したかったのですが、最終的に中途半端に終わりました。
やるべきことが多すぎて、ゴールがあまりに遠く感じたため、燃え尽きて再び興味を失ってしまいました。
PraneetはフルタイムでSizzyに入りたいとは思わなかったため、ここで彼とは別れました。

The final chance

私はまた旅行、カンファレンス、ワークショップの日々に戻りました。
さらにビデオブログも始めました。
それは充実していたし、旅行のせいでアプリに手を付ける暇がないという言い訳は完璧でした。

2019年のはじめのころ、Praneetにフルタイムで入ってくれないかと再びお願いに行きました。
何度も折衝したのち、彼は1年間の契約に同意し、アプリの作業に戻ってくれました。
収益を上げる目処も立っていないのに、自分の財布から毎月お金を支払わなければならないという行為が、私に言い訳を諦めさせる大きな動機付けになりました。
正直なところ、全てを私一人で行っていたとしたら、このアプリが日の目を見ることはなかったでしょう。

そのころカンファレンスとワークショップに明け暮れる日々から解放され、ようやく自宅で多くの時間を過ごせるようになりました。
私は自分のゾーンに戻ったのです。

客観的にはトラベルワーカーやノマドワーカーとして働いていた日々のほうが興味深いと思うかもしれませんが、しかし常に同じ場所で集中して働くという古いやり方のほうが、私にとってはより良かったと信じています。
子供達がマルコポーロで遊んでいる傍ら、プールサイドで仕事をしている写真を見ると、眩しくてまともに仕事ができたものではないとしか思えません。
それらはインスタ映えのために作られた偽物です。

ともあれ、私はようやくこのアプリをリリースすることを決意しました。

最初からドラッグアンドドロップツールを使ってランディングページを作っていれば、それは一日で完成していたかもしれませんが、しかし完璧主義者の私にとってそれは満足できるものではありませんでした。
私はそれをゼロから始めなければなりませんでした。
全てを最適化し、アニメーションを動かし、最後の細部に至るまで考え抜く必要がありました。
アプリを売るにはランディングページが大切だと考えています。
私はそれをTwizzyで証明し、IndieHackersでそれについて記事を書きました
ゼロマーケティングでありながら、イースターエッグで満たされたユニークなランディングページを作ったことで、人々にアプリを買い求めさせることに成功しました。

Sizzyのランディングページはようやく完成し、アプリは次の段階に進みました。

Oh boy, the payment part

私が現在拠点を置いているポーランドではStripeを利用できますが、EU居住者のVATを自動的に処理できるという理由で決済プロバイダとしてPaddleを選択しました。
オランダでPaddleを使うのに苦労し、私はまた言い訳ボックスに手を伸ばすところでした。

なぜならばアプリをElectronで作っていたため、ライセンスの関係でPaddleの公式SDKを使うことができなかったためです。
『数行のコードを追加する』で済んだはずの工数は、『全てを理解するための数週間』に変化しました。
文字どおり、支払いまわりのロジックをゼロから書き直す羽目になりました。
バックエンドをはじめ、ライセンスとサブスクリプションを管理するために別のアプリを作り、そしてライセンスの有効化無効化と、そしてそれらを検証するためのElectronコードを全て実装しました。
完成したアプリの機能だけ見ると、「なんだ、この程度自分でも数週間でできるよ」と思うかもしれませんが、しかしユーザが利用できる形で配布するまでにはさらに多くの作業が必要になりました。
もうアップデートさせないでくれ。

私はこのプロセスで多くのことを学びましたが、他のみんなにも同じ苦労を味わってほしくはありません。
私はこのインフラ全体をサービスとしてリリースする計画を立てており、いずれ開発者はElectronアプリで簡単にPaddleを利用することができるようになるでしょう。
リリースまでたった3年しかかかりません。

Deleting the GitHub repository

正直なところ、リポジトリを削除して、自分のGitHubプロフィールからプロジェクトを消去するのは、良い気分ではありませんでした。
少々気が引けましたが、肩書きを投げ捨てる必要がありました。
私は自分が何も悪いことをしていないと、自分を納得させなければなりませんでした。
このアプリは人々に2年半サービスを提供しましたが、その見返りはほぼ全くありませんでした。
現実を直視して、重要なことについて考えるときが来ました。

遂に来てしまいました。
私はここで、Mの付くワードに言及し、そして多くの読者を失うことでしょう。

Money

金は重要だ

ほとんどの開発者、さらにほとんどの人々は、金は何か悪いものであり、重要なことではない、などと思い込もうとしています。
金についての話はタブーなのです。

彼らは、今月は十分な収入がなかったり、支払いができなかったりといった不満を、友人同士でたびたび話しあいます。
彼らは会話が好きであり、みんな自分の貧乏自慢をしたがります。
さあ、あなたはそこで、今月たくさんのお金を稼いだことを伝えましょう。
緊張の糸が一気にぶった切れることでしょう。

たわごとなど、ききたくないわ!
お金は重要ではない。
その話をやめてください。
私は生きていけるだけの金は稼いでいる。
今はそのことについて考えたくない。
少なくとも私は学んでいる。
きっとある日魔法のように全てが変わるはずだ。
祖父が金持ちだから金儲けの才能があるのだろう。
それは運が良かっただけだ。
それで不満はないのかい?

素晴らしいツールを開発している才覚の高い開発者はたいへん多く、そして彼らは利益を上げることについて一瞬たりとも考えていません。
かつて私がより良いサラリーを求めて会社を辞めたとき、友人は私を貪欲だと評しました。
実にばかげた話です。
利益を考えなくてもよいのは高校生までの考え方であり、私は私の主張を世界中の開発者に広めたいと思っています。

オープンソース、ブログ記事の投稿、Lint設定やエディタのテーマの調整を一日中行うことは、全く問題ないことです。
家主があなたの部屋のドアを叩くか、スーパーに行ってレジで財布を取り出すまでのことですが。
あなたは毎日2時間を『少なくとも私はSVGについて多くを学んでいる』に費やしています。
そうですか。
そのような生活をどれだけ続けられるか、私はわかりません。
我々が10年後も今のような収益性の高く、甘やかされた業界に居続けられるのか、誰もわかりません。

03.jpeg
 ※新聞を読み上げる工場長

工場長は、ある日オーナーがやってきて彼をラジオに取り替えるまで、自分の仕事は安泰だと思っていました。
工場長には、ラジオが工場長の仕事を引き継ぐことを伝えるMediumもHacker Newsもありませんでした。
しかし開発者たちはみな、AIやMLが勢いを増し、自動化されつつある仕事の範囲がどんどん広がっていることを知っています。
我々はその事実を全く無視し、CSSで四角形を書いて高給を得る仕事が永遠に続くと信じています。
( プリンタを修理できる人は例外です。あなたは永久に仕事を得られるでしょう )

私はずっと前に、スターやライクの数に惑わされないことを覚えました。
私は今でもOSSが好きで、多くのアプリをOSS化していますが、しかし全てをOSS化しなければならないわけではありません。
おっと勘違いしないでください。
オープンソースであれば絶対に金を稼ぐことはできない、ということではありません。
コミュニティには素晴らしい人がたくさんいます。
生活のためにOSSをしている人もいます。
BabelやWebpackのように高額の報酬が提供されるプロジェクトも存在します。

しかし、ほとんどの開発者がOSSから稼ぐ金額は、ゼロです。
ゼロです。

私にはたくさんの友人がおり (友人とは、私のツイートに2回以上返信した人を表します) 、彼らは素晴らしいアプリやサービスを構築していて、様々な理由で報酬を得ずに問題を無料で解決しようとしていて、そしてIssueの海で溺れています。
どれほど残酷に聞こえようとも、これが悲しい現実です。
GitHub Sponsorsの登場で状況が変わることを本当に期待してはいますが、あくまでオプションでしかないため、有料のSaaSに人々は近寄らないでしょう。

私の思想は、世界中のユーザや企業の時間を大幅に節約できるツールを作るために多くの時間とお金を費やした人は、それだけの報酬を得るべきだというものです。

この点については、このトークでより詳しく解説しているので、興味があれば見てください。

えーと、この記事は何について話していたのでしたっけ。
そうそう、Sizzyです。

ローンチのときが近付きました。
あらゆる計算を行い、既存ユーザのほんの少しでも有料ユーザに移行してくれることを望んでいました。

時間です。

アプリのバージョン0.0.1を公開する準備が整いました。

テストは全て正常に終了しました。

アプリの全ての機能をデモンストレーションするビデオを作成しました。

Twitterにポストする準備ができました。

Product Huntに投稿する準備もできました。

あとはボタンを押すだけでしたが、それはたいへん抵抗のある作業でした。

ツールが無料でなくなったことにより、既存のユーザ全員が鎌を持って自宅の前で待ち構えることになるのではないかと心配していました。

しかし、実際に起こったことについては、心の準備が全くできていなかったのです。

The big launch

マジかよ!
これは完全に予想外だった!

SizzyはProduct Huntで2352票を得て、日間1位、週間1位、そして月間3位の評価を得ました。
Facebookの出したLibraや、Raspberry Pi 4よりも上の順位だったのです。
そんなバナナ?!

さらにリツイートが増えるように、リツイート者から3人に永久ライセンスをプレゼントするキャンペーンをTwitterで実施し、そして実際それはうまくいきました。
応募期間が終わった後、当選者を選ぶための適切なツールが見つからなかったのでLucky Retweetを作りました。
あなたも製品のいくつかをプレゼントしてみてください。
それがうまくいくことに驚くかもしれません。
最終的に3人だけではなく、30人に永久ライセンスをプレゼントしました。

知人のごく一部はののしり声をあげてリツイートしないかもしれませんが、当選者の全員は諸手を挙げて感謝するでしょう。
全体として、とてもポジティブな経験です。

最初のフィードバックは素晴らしいものでした。
最初の購入者が現れ始めたとき、とても信じられませんでした。
発売から1ヶ月半経ちましたが、購入者が現れるたびに同じ気持ちになります。

Haters are always louder

あなたとあなたのチームの時間を月あたり数百時間節約するために、一ヶ月に数ドル払うのは簡単なことです。
今から思えば、私はもっと高く値付けするべきでした。
Sizzyはすごい勢いで売れました。
ランディングページを見れば、Samsung、Bentley、Comcast、Toyota、Sketch、Hallmark、Basecamp、Algoliaといった錚々たる企業が並んでいることがわかるでしょう。

いったい何が起こったのですか?

ツールが時間を節約し、節約した時間で支払った以上の金額を作り出せることを理解できる人であれば、すぐにそれを買って使い始めるというだけのことです。

しかしながら、SaaSプロダクトの値付けのプロフェッショナルであらせられる方々は、より大声で拡散します。
実際の統計を知らずに、外部のコメントだけを見ていると、Sizzyは全然うまくいっていないように思えるかもしれません。

最もよくある彼らのご意見は次のようなものです。

「一回だけの買い切りにさせろ!!!111!11!」
「サブスクリプションにするほどのものではない」
「こんなの無料で配るべき」
「自分で同じもの作って無料配布するわ。こんな強欲企業ぶっ潰してやるぜ。I’m gonna make my own version and distribute it for free because screw these greedy companies that are charging users for something like this」(マジであったコメント)
「これGoogleChromeと同じじゃん」
「2007年にリリースされたChromeエクステンションと同じだから、それを使い続けるよ」

素晴らしいですね。
あなたがこれを使わないこと、あるいは他の類似品を使うことを、誰も止めたりはしませんよ。

衣料品店に入って叫んでみてください。
「これが20ドル?ばかじゃないの?隣の店に行って同じようなものを7ドルで買うわ。自分で縫ったらもっと安くできるわい。」

あるいはスーパーのレジで主張してください。
「この牛乳3ドルもするの?別の店なら2.5ドルで売ってるよ。それどころか自分で牛を飼ったら毎日無料で牛乳を飲めるよ!」

あなたは即座に警備員に叩き出されるでしょう。

しかし悲しいことに、インターネット界隈ではそんな言説が罷り通っています。

製品の値付けに納得がいかなければ、それを購入しないでください。
そのお金でかわりにトールサイズラテを買って、そして手動で画面サイズを変更するために毎週25時間使ってください。
君の実力を見せてやれ!

Focus on the customers

ありがたいことに、私は否定的なコメントを無視することを覚えていたので、無意味な論争に時間を浪費することはあまりありませんでした。

彼らを説得してアプリを購入させようと無駄な労力を費やすかわりに、既に購入した顧客が最高の価値を得られるように注力することにしました。
アプリの立ち上げ以降、我々は絶えずアプリの改善に取り組み、5回のバージョンアップで多くの新機能を盛り込みました。
またユーザがロードマップを確認し、欲しい機能に投票することができるようにTrello Boardも公開しました。

未読メールは山積みになり、私一人では到底捌ききることができなくなりました。
いくつかのツールを試してみましたが、いずれもうまくいきませんでした。
一人でどうにかするのは無理だ、と認識しなければなりませんでした。

プロジェクトのQAを手伝ってくれていた私のガールフレンドが、メールや他の管理タスクを手伝うと申し出ました。
一週間後、私は彼女をパートタイムとして雇い、仕事に使った時間の対価を適切に補償することに決めました。
一部の人々にとっては奇妙に聞こえるかもしれませんが、我々のような人種にとって、仕事と私生活の線引きをすることは、正直なところ困難です。
彼女はあらゆる種類のグッズを売ってプロジェクトを大きく宣伝することも欠かしませんでした。

04.png

顧客の全ての要望や苦情に速やかに対処したかったので、彼女の手助けは非常に有用でした。
問題のレポートを修正したあと、アップデートで問題が修正されたことを個別にメールします。
みんながこのカスタマサポートを気に入ってくれました。
このようなスタイルのまま大規模にスケールすることができないことはわかっていますが、できるだけ保てるように最善を尽くすつもりです。

Next steps

現在、Sizzyには1600人のユーザがいます。
これは驚くべきことです。
何故ならば、私がこれまでに行った唯一のマーケティングが、先週あるひとつのニュースレターにスポンサーしただけだからです。
流入のほとんどは、製品に満足したユーザの口コミです。
マーケティングに多くの投資を始める前に、アプリの安定性をさらに高めたいと考えています。
私は、Sizzyをあらゆる開発者やデザイナーが日常的に信頼して使用できるツールにするためのアイデアを幾つも持っており、最終目標にはまだまだ遠いものです。
いずれ100万人のユーザにリーチしたい。
誇大妄想に聞こえるかもしれませんが、私は自分がそこに辿り着けることを知っています。
私と目標の間に経っているものはただひとつ、私だけです。
しかし、私はもう二度と迷いません。
現在の私の焦点はSizzyであり、他の全ては二の次です。
たくさんの客が私に依存していることを知っているため、じっくりと熟睡することは難しいです。
奇妙な感じですが、しかし嫌な感覚ではありません。

次に何が起こるかはわかりません。
5つの大きな投資会社から話がありましたが、ベンチャーキャピタルに売り飛ばすのがいいのかよくわかりません
私は再び上司が欲しいとも思っているし、しかし上司がいたときは常に仕事で苦労していたからです。
スタートアップの100%を所有しているため、今はできるだけ投資を受けずに成長しようと考えています。
誰にも頼らずにそれを育てるというのは、味わったことのない感覚です。

スタートアップ!?
私、もしかしてスタートアップとか口走ってしまった?
私はこの言葉が大嫌いです。
木っ端開発者がこぞってハローワールドプロジェクトを完成させて速やかにTwitterのプロフィールを『XXのファウンダーCEO』に変更したため、スタートアップの正しい意味は完全に失われました。
私のプロフィールは妄言ではありません。
私はアプリを作っていて、あなたが私をどう呼ぶかは知ったことではありません。
プロフィールを変更しても現実が変わることはありません。

おっと、自分語り記事がこき下ろし記事に化ける前に、これ以上の言及は辞めておくことにします。

Conclusion

  1. 自分自身の問題を解決する。
  2. そのソリューションを他者にすぐに見せる。
  3. 速やかに パッケージ化して配布する (自己批判)
  4. 売ることを怖がったり恥じたりせず、そしてお金を払わせたからにはがっかりさせないこと。
  5. プロダクトに価格を付けるのはあなたの仕事であり、他人の言うことなど聞く必要はない。

プロダクトを有償で販売したことに対して他の開発者などがとやかく言ってくるせいで、あなたが次のプロダクトを無償提供してしまうのではないかと危惧しています。

罠に落ちてはいけません!

あなたの仕事に価値があるならば、いくらアンチが声高に非難してきたとしても、それ以上にあなたのプロダクトに喜んでお金を払う顧客を多く得られることでしょう。

かつて有名なレジェンドは言いました。

Cause the players gonna play, play, play, play, play
And the haters gonna hate, hate, hate, hate, hate
Baby, I'm just gonna shake, shake, shake, shake, shake
I shake it off, I shake it off

何かもっと話したいことがあるようでしたら、TwitterのDMなどにどうぞ。

また次の記事で会いましょう👋️

Thanks to Marija Stamadzieva.

コメント欄

「これまでに見たMediumの記事で最高の読み物だ。」
「ちょうどフロントエンドの仕事を得たばっかりでSizzyを購入するとこだったので、Sizzyの100万分の数千のうちひとりになれたみたい。」
「今ReactNativeの仕事やってるけど、Webに戻ったときには是非使うわ。」
「"しかし悲しいことに、インターネット界隈ではそんな言説が罷り通っています" 妻が最近リアル店舗を開いたのだが、同じことを対面で言われた回数は数知れないよ」
「"価格は自分で決めろ" 素晴らしいアドバイスをありがとう」
「結局この世のほとんどは無料ではないのだから、誰かがお金を出さないと何れ持続不能になる」
「あなたの製品は素晴らしいから、その仕事で報酬を受け取るのはよいことだ。」
「あなたは金を出さない顧客を失ったが、金を出さない顧客は何の役にも立たないから切り捨てて正しい。」
「自分もChrome拡張で同じ目に会ったわ。色々やったけど結局邪魔な返信は全削除するのが一番。」
「その引き出しに手を出したくなったとき、この記事を思い出すことにします。」
「開発者が忘れがちな、収益化やビジネスといった要素を思い出させてくれてありがとう。」
「OSSで金を稼ぐことはほとんどできない。それどころか無償で公開したとしてもアンチで溢れ、皆を喜ばせることは難しい。」
「Sizzyのページを見たけど、機能と経験談しか載ってない。これは何をするものなのか、どうしてこれを使うべきかみたいなのも書いてほしい。」
「投資家は高値で売ることしか考えてないから、投資を受けることはプロダクトの使用者にとっていいことだとは思えない。」

コメント欄もほぼ絶賛でした。

訳注

Open Collective:一回のみ、毎月といった単位で寄付できる投げ銭プラットフォーム。
GitHub Sponsors:GitHubに展開しているOSS開発者に寄付できる、GitHub公式の投げ銭サービス。
Stripe / Paddle:オンライン決済代行サービス。
Insomnia:APIの動作確認ができるRESTクライアント。類似品としてPostmanPostwomanなど。
マルコポーロ:プールで行う鬼ごっこのような遊びらしい。東方見聞録とは全く関係ない。
Twizzy:メニューバーからTweetやDMできるiPhoneアプリのようだ。
VAT:付加価値税。日本の消費税に相当。国毎に税率が違うのでそのへんの対応が面倒らしい。
Lucky Retweet:ツイートと当選者数を入れると、リツイート者の中から当選者を選んでくれるサービス。
Trello:シンプルでわかりやすいタスク管理ツール。
有名なレジェンド:Taylor SwiftShake It Off。全くどうでもいいが私はこの人を知らなかった。

感想

私はスマホ向けWebサイトを作っていないので(そもそもフロントエンドエンジニアではない)Sizzyも購入していないのですが、そういう要件があったとしたら真っ先に購入すべきアプリであることは間違いありません。
記事中では1600ライセンスということでしたが、軽く数万は売れてもおかしくないであろう優れたツールです。
しかしさすがに100万は言い過ぎな気がしますね。
そもそもそんなにフロントエンドエンジニアいるんですかね?

OSSは市場として最悪なターゲットだというのはよく知られている話です。
日本でもカンパウェアやビールウェアといった任意寄付形式のソフトウェアはすっかり廃れました。
残っているのは最初から有料のソフト、フリーミアムや基本無料といった追加コンテンツ形式、およびユーザ以外から徴収する広告タイプといった、ユーザの良心に一切期待しないタイプばかりです。
この開発者やこれらのアプリは、五月蠅いだけで金も出さない何の役にも立たない連中を切り捨てて、有料アプリに金を出してくれる優良ユーザを囲い込むことで成功した例と言えるでしょう。

もっとも、元のアプリが優れたものであるというのが大事な前提なのですけどね。

私もなんかアプリ売ってだらだら暮らしたいわー。
でも売れるアプリを作るのもめんどいから誰か作って全権利を無償で譲ってほしいわー。

ところで"isn't that bananas?!"って「そんなバナナ?!」以外になんと訳せばいいんだろう。

【PHP8.0】PHP8で警告のエラーレベルが軒並み厳しくなる

$
0
0

多くの警告について、PHP8.0でエラーレベルが変更されます。

これはReclassifying engine warningsというRFCで受理されたものです。
提案者はいつものNikita。
影響の大きい未定義変数アクセスについては個別に紹介しましたが、ここではそこで紹介しなかった細かい警告について見ていきます。

これまでE_NOTICEだった警告の一部がE_WARNINGに、これまでE_WARNINGだった警告の一部が例外になります。
E_WARNINGを抑制するような書き方をしている場合、PHP8では動かなくなる可能性が高いので気をつけましょう。
現在E_NOTICE以下であればいきなり動かなくなることはありませんが、そもそも抑制する書き方がよくないので、なるべく修正した方がよいでしょう。

エラーレベルの変更がない警告も並んでいるので、もしかしたら全警告が列挙されてるのか?と思ったのですが、expected to be a %s, %s givenとか色々無いものもあるので、全てを出しているわけではないようです。
どういう基準なんだろうか?

Reclassifying engine warnings

Attempt to increment/decrement property '%s' of non-object

E_WARNING → Error exception

オブジェクトではない変数のプロパティをインクリメント/デクリメントすると発生する。

    $a = 1;
    $a->b++;

Attempt to modify property '%s' of non-object

E_WARNING → Error exception

オブジェクトではない変数のプロパティを変更すると発生する。

    $a = 1;
    $a->b['c'] = 1;

Attempt to assign property '%s' of non-object

E_WARNING → Error exception

オブジェクトではない変数にプロパティを追加すると発生する。

    $a = 1;
    $a->b = 1;

このへん全部同じでいいんじゃないか?

Creating default object from empty value

E_WARNING → Error exception

未定義の変数にプロパティを追加すると発生する。

    $a->b = 1;

PHP7.3では$aが定義されるのだが、PHP8では何も定義されなくなると思われる。

ところで未定義の変数のプロパティを変更しようとすると何の警告もなくオブジェクトが生成されるのだが、こっちは今後もいいのだろうか。

    $a->b['c'] = 1; // エラー出ない
    var_dump($a); // object(stdClass)#1 (1) { ["b"]=> array(1) { ["c"]=> int(1) } }

Trying to get property '%s' of non-object

E_NOTICE → E_WARNING

オブジェクトではない変数のプロパティを参照すると発生する。

    $a = 1;
    $a->b;

Undefined property: %s::$%s

E_NOTICE → E_WARNING

オブジェクトの未定義プロパティを参照すると発生する。

$a = new stdClass();
$a->b;

PHPの場合、入力として外部引数やらAPIやらを使うことが多いため、読み取りの失敗については書き込みより寛容気味。

Cannot add element to the array as the next element is already occupied

E_WARNING → Error exception

配列の自動挿入による整数キーがPHP_INT_MAXを超えたときに発生する。

    $a = [
        PHP_INT_MAX => 1,
    ];
    $a[] = 2;

ちなみに計算値で指定すれば、PHP_INT_MAXを超えていてもいける。

    $a = [
        PHP_INT_MAX => 1,
    ];
    $a[PHP_INT_MAX+1] = 2; // -2147483648とかになる

Cannot unset offset in a non-array variable

E_WARNING → Error exception

エラーの出し方がわからない

Cannot use a scalar value as an array

E_WARNING → Error exception

文字列型ではないスカラー型の変数に配列値を追加すると発生する。

    $a = true;
    $a[] = 1;

文字列型の場合は文字単位アクセスという正しい文法。

ちなみにnullで初期化した場合は問題なく動く。

    $a = null;
    $a[] = 1; // [ 0 => 1]

なぜかfalseでも動く。

    $a = false;
    $a[] = 1; // [ 0 => 1]

Trying to access array offset on value of type %s

E_NOTICE → E_WARNING

文字列型ではないスカラー型の変数を配列形式で読み込もうとすると発生する。

    $a = true;
    $a[1];

このE_NOTICE自体PHP7.4で追加されたもので、それ以前は何も出さずにnullを返していた。

Only arrays and Traversables can be unpacked

E_WARNING → TypeError exception

関数呼び出し時の引数展開にiterableでない値を渡すと発生する。

    var_dump(...1);

unpackとは特に関係ない。

Invalid argument supplied for foreach()

E_WARNING → TypeError exception

iterableでない値をforeachすると発生する。

    $a = 1;
    foreach($a as $loop){}

Illegal offset type

E_WARNING → TypeError exception

配列のキーに配列やオブジェクトを指定すると発生する。

$a = [
    new stdClass() => 1,
    [] => 2,
];

Illegal offset type in isset or empty

E_WARNING → TypeError exception

issetおよびemptyでチェックする配列のキーに配列やオブジェクトを指定すると発生する。

    $a = [];
    isset($a[new stdClass()]);

ちなみに$aが未定義やスカラー型の場合は何のエラーも起こらない。

    isset($a[new stdClass()]); // エラー出ない
    $a = 1;
    isset($a[new stdClass()]); // エラー出ない

未定義やint型等であれば配列形式アクセスした時点でfalseだから中身を見る必要もないというのはわかるが、文字列型でもエラーが出ない理由はよくわからない。

    $a = 'a';
    isset($a[1]); // true
    isset($a[new stdClass()]); // false エラー出ない

Illegal offset type in unset

E_WARNING → TypeError exception

unsetする配列のキーに配列やオブジェクトを指定すると発生する。

    $a = [];
    unset($a[new stdClass()]);

$aが未定義の場合Illegal offset typeは発生しないが、かわりにUndefined variableのE_NOTICEが出る。
文字列以外のスカラー型には何のエラーも出さず、文字列型やオブジェクトにはFatal errorが発生する。

    unset($a[new stdClass()]); // E_NOTICE: Undefined variable
    $a = 1;
    unset($a[new stdClass()]); // エラー出ない
    $a = 'a';
    unset($a[new stdClass()]); // Fatal error: Cannot unset string offsets
    $a = new stdClass();
    unset($a[new stdClass()]); // Fatal error: Cannot use object of type stdClass as array

このあたりの法則はさっぱりわからない。

Indirect modification of overloaded element of %s has no effect

E_NOTICEのまま

SplFixedArrayに突っ込んだ配列の値を直接変更すると発生する。

    $a = new SplFixedArray(1);
    $a[0] = [1];
    $a[0][0] = 2;

値を変更しているつもりだが、実際には変更されていないという注意。

SplFixedArrayに限らず、ArrayAccessをimplementsしたクラスに一般的に発生する症状のようだ。

Indirect modification of overloaded property %s::$%s has no effect

E_NOTICEのまま

マジックメソッド__getが配列を返す場合、その返り値を直接変更すると発生する。

    class A{
        private $value = ['a' => 1, 'b' => 2];
        public function __get($k){
            return $this->value;
        }
    }

    $a = new A;
    $a->value['a'] = 3;

こちらも値を変更したつもりだが、実際には変更されていない。

なお配列ではなくオブジェクトであれば、エラーも出ないし値を直接変更できてしまう。

    class A{
        private $obj;
        public function __construct(){
            $this->obj = new stdClass();
        }
        public function __get($k){
            return $this->obj;
        }
    }

    $a = new A;
    $a->obj->b = 1;

    var_dump($a); // { 'obj' => stdClass{ 'b'=>1 } }

Object of class %s could not be converted to int/float/number

E_NOTICEのまま

オブジェクトをスカラー型にキャストすると発生する。

    (int)new stdClass();

緩い比較が内部的にこのキャストを使用しているため、オブジェクトとスカラー型を緩く比較するとE_NOTICEが発生する。

    $a = new stdClass();
    var_dump($a == 1); // E_NOTICE
    var_dump($a === 1); // エラー出ない

比較ではエラーが出るべきではないので、こちらの問題がどうにかなるまでエラーレベルを変更しない。

A non-numeric value encountered

E_WARNINGのまま

次項で一緒に解説する。

A non well formed numeric value encountered

E_NOTICEのまま

非数値文字列を数値演算すると発生する。

    1 + '1';  // エラー出ない
    1 + '1a'; // E_NOTICE: A non well formed numeric value encountered
    1 + 'a';  // E_WARNING: A non-numeric value encountered

完全に数値形式の文字列ではエラーは出ず、一部だけ数値として評価できるときはnon well formed numeric value、完全に数値でない場合はnon-numeric valueになる。
今回はエラーレベルが変わらないが、数値形式文字列の計算は安全のためキャストしておいた方がよいだろう。

    1 + (int)'a'; // エラー出ない

Accessing static property %s::$%s as non static

E_NOTICEのまま

staticプロパティにインスタンスからアクセスすると発生する。

    class A{
        public static $property = 1;
    }

    $a = new A();
    $a->property;

正しくは$a::$property、もしくはA::$property
インスタンス内部からであればself::$propertyもいける。

Array to string conversion

E_NOTICE → E_WARNING

配列を文字列型にキャストすると発生する。

    (string)[];

変換前の配列の中身がどうなっていたとしても変換後の文字列はArrayになるので、実質的に機能していない状態なのでExceptionでもいい気がする。

Resource ID#%d used as offset, casting to integer (%d)

E_NOTICE → E_WARNING

リソースIDを配列のキーとして使用すると発生する。

    $fp = fopen('hoge', 'w+');
    $array = [$fp => $fp];
    var_dump($array); // []

リソースIDは整数っぽい値であり、かつプログラム中ではユニークなので、このような使い方ができそうではあるが実際は動いていない。
明示的にキャストするとint型になるため警告は発生せず、正しく動作する。

    $fp = fopen('./hoge', 'w+');
    $array = [(int) $fp => $fp];
    var_dump($array); // [1=>resource]

そもそも動いてないので、これもいきなりExceptionでいい気がしないでもない。

String offset cast occurred

E_NOTICE → E_WARNING

文字列への角括弧オフセットアクセスのキーに整数ではない数値を使ったときに発生する。

    'string'[1.5];
    'string'[true];

下の項目と同じような内容なのでエラーレベルを揃えたという話のようだ。

Illegal string offset '%s'

E_WARNINGのまま

文字列への角括弧オフセットアクセスのキーに数値ではない値を使ったときに発生する。

    'string'['a'];

Uninitialized string offset: %d

E_NOTICE → E_WARNING

文字列への角括弧オフセットアクセスで範囲外の値を読み込もうとしたときに発生する。

    'string'[10];

Illegal string offset: %d

E_WARNINGのまま

文字列への角括弧オフセットアクセスでマイナスの範囲外の値を変更したときに発生する。

    $str = 'string';
    $str[-10] = 'a';

正の範囲外を変更したときは単に文字列が伸びるだけでエラーは発生しない。

    $str = 'string';
    $str[10] = 'a'; // 'string    a'

Cannot assign an empty string to a string offset

E_WARNING → Error exception

文字列への角括弧オフセットアクセスで値を空文字に変更しようとしたときに発生する。

    $str = 'string';
    $str[1] = '';

2文字以上与えた場合は2文字目以降が無視されるだけでエラーは発生しない。

    $str = 'string';
    $str[1] = 'abcde'; // 'saring'

Only variables should be passed by reference

E_NOTICEのまま

リファレンス関数に値を直接渡すと発生する。

    sort([2, 1]);

Only variable references should be returned by reference

E_NOTICEのまま

リファレンス返しで値を直接返すと発生する。

    function &ref(){
        return 1;
    }
    ref();

Only variable references should be yielded by reference

E_NOTICEのまま

リファレンス返しで値を直接yieldすると発生する。

    function &ref(){
        yield 1;
    }
    foreach(ref() as $v);

リファレンス返しは百害しかないので使用してはならない。

Only variables should be assigned by reference

E_NOTICEのまま

リファレンスではない関数をリファレンスで受け取ろうとすると発生する。

    function ref(){
        return 1;
    }
    $x = &ref();

Attempting to set reference to non referenceable value

E_NOTICEのまま

出し方がわからないどころか、事例すら一切出てこない謎の警告。

Cannot pass by-reference argument %d of %s%s%s() by unpacking a Traversable, passing by-value instead

E_WARNINGのまま

参照渡し関数にTraversableな値を引数アンパックして渡すと発生する。

    function ref(&$var){}
    ref(...new ArrayIterator([1]));

いみがわからない。

Division by zero

E_WARNING → DivisionByZeroError exception

数値を0で割ると発生する。

    1 / 0;

PHP7.4までは計算結果がfloat(INF)になる。

Undefined variable

E_NOTICE → E_WARNING

未定義変数を参照すると発生する。

    echo $a;

詳細は個別記事を参照のこと。

Undefined array index

E_NOTICEのまま

配列の未定義キーを参照すると発生する。

    $a = [];
    echo $a[1];

詳細は個別記事を参照のこと。

感想

そもそもどうすれば出せるのかすらわからないエラーがあった。

警告に寛容なプログラミングをしている場合、Invalid argument supplied for foreachCreating default object from empty valueあたりはよく見かけるのではないかと思います。
これらはPHP8では例外になって完全に動かなくなるので注意しましょう。

それ以外でも、ゆるふわぺちぱーに対する締め付けは年々厳しくなる一方で、彼らの肩身はどんどん狭まりつつあります。
かつてはPHP以上にアバウトで破壊と慈悲の混沌だったJavaScript界も、最近は型に嵌まっていないゆるふわJavaScripterを完全排除する流れができあがっています。
やがて彼らの居場所が完全に失われてしまったとき、難民たちはいったいどこに行くのでしょうね。

配列のグループ分け

$
0
0

配列のグループ分け

Laravelを使って班分け(グループ分け)してみたというのを見たのですが、devide関数ややこしくない?

PHPには配列を分割するarray_chunkという関数が最初から用意されているのですが、参照先の参照先にもあるように末尾処理が微妙です。
10個の配列を4分割すると[3個, 3個, 3個, 1個]になってしまうのです。

そんなわけで元記事ではarray_sliceで切り出しているのですが、正直なところわかりにくいので、もっと簡単にやりましょう。

    /**
     * 配列を分割して返す
     * @param array 元配列
     * @param int 分割数
     * @return array 元配列を分割したやつ
     */
    function divide(array $arr, int $division):array{
        $ret = [];
        $cnt = 0;
        foreach ($arr as $k => $v) {
            $ret[$cnt++ % $division][$k] = $v;
        }
        return $ret;
    }

    $a = range(0, 9);
    shuffle($a);
    $ret = divide($a, 4); // [3個, 3個, 2個, 2個]

できた。

元記事ではしていなかったのですが、一応キーも保持するようにしています。

おわりに

作ってから気付いたんだけど動作違うわこれ。

array_chunkや元記事は、分割後の配列も並んでいる順に整列します。
ABCDEFGHIJが並んでいたら[ABC, DEF, GH, IJ]となります。

対してこちらは、レジに並ぶ客のように分かれます。
ABCDEFGHIJ[AEI, BFJ, CG, DH]になるということです。

そんなわけで元記事をそのまま差し替えるには至りませんでした。
残念。

ここまで全部間違い

以下2019/10/24追記

ここまでリリースしたあとで突っ込まれたわけですが、元記事を読み返してみたら設問が根本的に違う。

・1組あたりの人数は4人で、グループ数は可変。
・端数が出たら5人組にする。
・人数が少ない場合は特殊処理。

いったい何をキメていたら、この設問から上のdivide()みたいな関数を作れるんですかね??

ということで作りなおしましょう。
人数が少ないときの挙動がよくわからないので列挙しておきます。

・5人以下:[n]
・6人:[3, 3]
・7人:[4, 3]
・8人:[4, 4]
・9人:[5, 4]
・10人:[5, 5]
・11人:[4, 4, 3]
・12人:[4, 4, 4]
・13人以上:定義通り

グループあたり人数を可変にするとこのあたりの動作がよくわからなくなるので、以下では4人固定としておきます。

/**
 * 配列を分割して返す
 * @param array 元配列
 * @return array 元配列を分割したやつ
 */
function divide(array $arr): array {
    $cnt = count($arr);

    // 6未満
    if ($cnt < 6) {
        return [$arr];
    }

    // 11未満
    if ($cnt < 11) {
        return array_chunk($arr, ceil($cnt / 2));
    }

    // 4の倍数と11
    if (!($cnt % 4) || $cnt === 11) {
        return array_chunk($arr, 4);
    }

    // それ以外
    $ret = array_chunk($arr, 4);
    $extra = array_pop($ret);
    $cnt = 0;
    foreach ($extra as $v) {
        $ret[$cnt++][] = $v;
    }
    return $ret;
}

// 確認
for ($a = 1; $a < 20; $a++) {
    var_dump( divide(range(1, $a)) );
}

うーん、元記事とたいして変わってない。
これならわざわざ書き換える意味がないかんじですね。

先にグループ数を求めてから配分したら多少行数が減るかな?

function divide(array $arr): array {
    // 分割数
    $cnt = count($arr);
    if ($cnt < 6) {
        $division = 1;
    } elseif ($cnt < 11) {
        $division = 2;
    } elseif ($cnt < 12) {
        $division = 3;
    } else {
        $division = floor($cnt / 4);
    }

    // 分割する
    $cnt = 0;
    foreach ($arr as $k => $v) {
        $ret[$cnt++ % $division][$k] = $v;
    }
    return $ret;
}

行数は減りましたが、読みやすくなったかというとどうでしょうね。
というか11が厄介だな。

やろうと思えば$divisionの算出は1行でできますが、さらにわかりにくくなるだけなので辞めましょう。

やろうと思ったら読めなくなった
function divide(array $arr): array
{
    $cnt = 0;
    foreach ($arr as $k => $v) {
        $ret[$cnt++ % (($c = count($arr)) < 12 ? $c < 11 ? 1 + floor($c / 6) : 3 : floor($c / 4))][$k] = $v;
    }
    return $ret;
}

さて、今度こそ設問に間違いはないよな?よな?


JavaScriptの配列操作に役立つ13のヒントとトリック

$
0
0

以下はDuomly ( Webサイト / Twitter )による記事、13 useful JavaScript array tips and tricks you should knowの日本語訳です。

Duomlyはプログラミング学習コースを提供しているWebサイトです。
ただランディングページにたいしたことが書いてないので、詳細はよくわかりません。
ここはもう少し書いておいてほしいところ。

13 useful JavaScript array tips and tricks you should know

配列はJavaScriptで最も一般的な概念のひとつであり、内部に格納されたデータを操作する手段は多数存在します。
配列はJavaScriptの最も基本的なトピックであり、プログラミング学習パスの最初に学習するものだという前提のうえで、この記事ではあなたが知らないかもしれない、しかしコーディングにおいてはとても役立つかもしれない、幾つかのトリックを紹介します。
さっそく始めましょう。

1. Remove duplicates from an array

配列から重複した値を削除する方法は、JavaScriptの質問の中でも非常によく見かけるものです。
ここでは、新しめの機能であるSet()を使った高速で簡単な解決策を紹介します。
これには2種類、.from()を使う方法、およびスプレッド構文...を使う方法があります。

    var fruits = ["banana", "apple", "orange", "watermelon", "apple", "orange", "grape", "apple"];

    // fromを使う方法
    var uniqueFruits = Array.from(new Set(fruits));
    console.log(uniqueFruits); // ["banana", "apple", "orange", "watermelon", "grape"]
    // ...を使う方法
    var uniqueFruits2 = [...new Set(fruits)];
    console.log(uniqueFruits2); // ["banana", "apple", "orange", "watermelon", "grape"]

簡単ですね。

2. Replace the specific value in an array

コーディング中に、配列の一部の値だけを書き換えなければならない事態に陥ることはよくあります。
これを実現するとても短い記法があるのですが、もしかしたらご存じないかもしれません。
このために.spliceを使うことができます。
これは3つの引数を取り、ひとつめが変更する開始位置のインデックス、ふたつめが変更する数、そして3つめ以降は存在すればかわりに新しい値を入れ込みます。

    var fruits = ["banana", "apple", "orange", "watermelon", "apple", "orange", "grape", "apple"];
    fruits.splice(0, 2, "potato", "tomato");
    console.log(fruits); // ["potato", "tomato", "orange", "watermelon", "apple", "orange", "grape", "apple"]

3. Map array without .map()

全員が.map()を使いこなしているだろうと思いますが、.map()を使わずに同じ効果を得られる全く別のソリューションが存在します。
それは.from()メソッドです。

    var friends = [
        { name: 'John', age: 22 },
        { name: 'Peter', age: 23 },
        { name: 'Mark', age: 24 },
        { name: 'Maria', age: 22 },
        { name: 'Monica', age: 21 },
        { name: 'Martha', age: 19 },
    ]

    var friendsNames = Array.from(friends, ({name}) => name);
    console.log(friendsNames); // ["John", "Peter", "Mark", "Maria", "Monica", "Martha"]

4. Empty an array

要素が大量に入った配列があるのですが、何らかの理由で全ての要素を削除したいとします。
要素をひとつひとつ削除したくないですか?
それを行うことは、わずか1行でできます。
lengthを0にすると中身が全部消えます。

    var fruits = ["banana", "apple", "orange", "watermelon", "apple", "orange", "grape", "apple"];

    fruits.length = 0;
    console.log(fruits); // []

5. Convert array to an object

今手元にひとつの配列がありますが、諸事情により同じデータを持つオブジェクトが必要になりました。
配列をオブジェクトに変換する最も手っ取り早い方法は、スプレッド構文を使用することです。

    var fruits = ["banana", "apple", "orange", "watermelon"];
    var fruitsObj = { ...fruits };
    console.log(fruitsObj); // {0: "banana", 1: "apple", 2: "orange", 3: "watermelon"}

6. Fulfill array with data

いちから配列を作成して何らかのデータで埋めたい場合、あるいは同じ値の並んだ配列が必要な場合、.fill()メソッドがその機能を持っています。

    var newArray = new Array(10).fill("1");
    console.log(newArray); // ["1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1"]

7. Merge arrays

.concat()を使わずに複数の配列をひとつにマージする方法を知っていますか?
その手段がここにはあります。
薄々お察しかとは思いますが、スプレッド構文...は配列操作したいときに非常に便利であり、今回も同じです。

    var fruits = ["apple", "banana", "orange"];
    var meat = ["poultry", "beef", "fish"];
    var vegetables = ["potato", "tomato", "cucumber"];
    var food = [...fruits, ...meat, ...vegetables];
    console.log(food); // ["apple", "banana", "orange", "poultry", "beef", "fish", "potato", "tomato", "cucumber"]

8. Find the intersection of two arrays

ふたつの配列から共通項を抽出する処理は、JavaScriptの面接において直面する可能性の高い課題のひとつです。
あなたが配列のメソッドを理解しているか、ロジックを構築できるかの能力を見ることができるからです。
ここまでに紹介したメソッドのひとつを使うことで、ふたつの配列の共通部分を見つけることができます。
またチェックしている配列の値が重複していないことを確認するためには.filter.includesメソッドを使用します。

    var numOne = [0, 2, 4, 6, 8, 8];
    var numTwo = [1, 2, 3, 4, 5, 6];
    var duplicatedValues = [...new Set(numOne)].filter(item => numTwo.includes(item));
    console.log(duplicatedValues); // [2, 4, 6]

9. Remove falsy values from an array

まず"falseっぽい値"をはっきりさせましょう。
JavaScriptにおいて"falseっぽい値"とは、false、0、''、null、NaN、undefinedです。
これでfalseっぽい値を配列から削除する方法がわかりました。
それは.filter()メソッドです。

    var mixedArr = [0, "blue", "", NaN, 9, true, undefined, "white", false];
    var trueArr = mixedArr.filter(Boolean);
    console.log(trueArr); // returns ["blue", 9, true, "white"]

10. Get random value form the array

配列からランダムに値を選択しなければならない状況に追い込まれることが人生にはよくあります。
簡単高速で短くクリーンなコードを保つには、配列長からランダムなインデックス番号を生成するとよいでしょう。

    var colors = ["blue", "white", "green", "navy", "pink", "purple", "orange", "yellow", "black", "brown"];
    var randomColor = colors[(Math.floor(Math.random() * (colors.length)))]

11. Reversing an array

配列を逆さにしたい場合、複雑なループと関数を使って新たな配列を作る必要はありません。
最初から専用のメソッドが用意されているので、1行で終わらせることができます。

    var colors = ["blue", "white", "green", "navy", "pink", "purple", "orange", "yellow", "black", "brown"];
    var reversedColors = colors.reverse();
    console.log(reversedColors); // ["brown", "black", "yellow", "orange", "purple", "pink", "navy", "green", "white", "blue"]

12. .lastIndexOf() method

JavaScriptには、指定された要素が最後に出現する位置のインデックスを返すという興味深いメソッドが存在します。
たとえば配列内に重複した値が存在する場合、それが最後に出現する位置を見つけることができます。

    var nums = [1, 5, 2, 6, 3, 5, 2, 3, 6, 5, 2, 7];
    var lastIndex = nums.lastIndexOf(5);
    console.log(lastIndex); // 9

13. Sum all the values in the array

面接で頻繁に聞かれる、もうひとつの質問です。
特に難しいことはなく、.reduceメソッドひとつで解決することができます。

    var nums = [1, 5, 2, 6];
    var sum = nums.reduce((x, y) => x + y);
    console.log(sum); // 14

Conclusion

この記事では、ちょっとしたコーディングに役立ち、コードを短く簡潔に保つための13のヒントとトリックを紹介しました。
配列だけではなくそれ以外のデータ型にも、調べてみる価値のある様々な裏技があることを忘れないでください。
この記事で紹介したソリューションをあなたが気に入って、開発プロセスの改善に役立つことを願っています。

Have a nice coding!

コメント欄

「Awesom!!」「Thanks!」
length=0知らんかった、すごい!」

「Array.fromの第二引数知らなかった、勉強になる。」「Array.map()のかわりに常にArray.from()を用いなければならない、というのはよくわからない。」

「“”は"に修正すべき。」「そのとおり、記事にコードを含めるときは有効なコードであることを確認しよう。」

「3番目は配列っぽいオブジェクト、NodeListとかに使うとより便利だよね。」

「8番目、Array.prototype.includesは線形でSet.prototype.hasは定数だから、大きい配列比較にはvar duplicatedValues = […new Set(numOne)].filter(item => numTwo.includes(item));のかわりにlet firstValues = [...new Set(numOne)];let duplicatedValues = numTwo.filter(item => firstValues.has(item));ってしたほうがいい」「firstValuesはhas持ってないから動かないよ。」「ほんとだ間違えた、正しくはlet firstValues = new Set(numOne);だった。」

「11番目、reverse()はcolors自身も変更してしまう。変更したくないときは[...colors].reverse();ってしよう。」「colors.slice().reverse()でもいいぞ。」

感想

元記事は“”が全角で入っていたので、全て半角に修正しています。
英語圏の人が手動でとか入れたり、さらに全角ダブルクォートの開きと閉じを使い分けるとは考えにくいですし、そもそも構文エラーなので、何らかの自動変換ライブラリが誤爆しているのだと思われます。
なかなか不思議なライブラリもあったものですね。

あとvarももう流行らないのでletとかにしておいてほしいですね。

とはいえ細かい(パースエラーは細かくないが)点を除けば、配列を実用的に操作したいときに役立つトピックだと思います。
自分自身をさくさく書き換えているので何か言いたい人も出てきそうですが、そんなものより目の前で動くコードが必要なんですよ。

補足

Array.spliceは、3番目以降の引数があれば削除位置に挿入される。
削除した数以上を指定しても全て挿入され、その後のインデックスが全部ずれる。
3番目以降の引数がなければ単に削除される。

    var fruits = ["banana", "apple", "orange", "watermelon", "apple", "orange", "grape", "apple"];

    fruits.splice(0, 2, "a", "b", "c", "d");
    console.log(fruits); // ["a", "b", "c", "d", "orange", "watermelon", "apple", "orange", "grape", "apple"]

    fruits.splice(0, 9);
    console.log(fruits); // ["apple"]

配列要素を全削除するには、変な参照とかでもないかぎり、単純に上書きするほうが適切だろう。
結果が同じとはいえ、length=0は直感的操作とはあまり思えない。

    var fruits = ["banana", "apple", "orange", "watermelon", "apple", "orange", "grape", "apple"];
    fruits = [];
    console.log(fruits); // []

falseっぽい値false0''nullNaNundefinedのうち、NaNだけ==falseがfalseになる。
NaNは自分自身も含めたあらゆる値との比較がfalseになるので仕様なのだがちょっとわかりにくい。

【PHP8.0】PHPでunion型が使えるようになる

$
0
0

Union Types 2.0というRFCが投票中です。
提案者はまたまたのNikita。
2019/10/25開始、2019/11/08終了で、受理には2/3+1の賛成が必要です。
2019/11/04時点で賛成55反対5であり、ほぼ導入確定です。

PHPのunion型って何かというと、TypeScriptのunion型とだいたい同じです。
int|string $aと書いたら$aint型もしくはstring型ですよ、ということです。

ちなみに別途RFCをGitHubで管理しようという実験が進行中で、このRFCの詳細はGitHub上に存在します
このRFCはまだNikitaの個人GitHub上にしかないのですが、本決まりになったらPHP公式に移動になると思います。
まあGitHubのほうが管理とか更新とか楽ですからね。
ただGitHubはURLがすぐ404になるのだけはどうにかしてほしい。

Union Types 2.0

Introduction

union型は、単一の型ではなく、複数の異なる型の値を受け入れます。
PHPは既に、2種類の特別なunion型をサポートしています。

・null許容型。?Tで使える。
・array型もしくはTraversable型。iterableで使える。

しかし任意のunion型はサポートされていません。
現在はかわりにphpdoc注釈を書くくらいしかできません。

class Number {
    /**
     * @var int|float $number
     */
    private $number;

    /**
     * @param int|float $number
     */
    public function setNumber($number) {
        $this->number = $number;
    }

    /**
     * @return int|float
     */
    public function getNumber() {
        return $this->number;
    }
}

Statisticsセクションで、オープンソースでunion型がどれだけ普及しているかを示しています。

言語でunion型をサポートすることにより、より多くの型情報をphpdocに頼ることなく関数シグナチャに移動することができ、多数の利点が得られます。

・型は実際に強制されるため、ミスを早期に発見できる。
・エッジケースを見逃したり、仕様変更の際にドキュメントを更新し忘れたりする可能性が減る。
・継承時にもリスコフの置換原則を適用できる。
・リフレクションから利用できる。
・phpdocより分量が減らせる。

union型は、ジェネリクスと並んで型システムに残っている最大の穴です。

Proposal

union型を構文T1|T2|...で表し、型を書くことができる全ての位置でunion型を使用可能とする。

class Number {
    private int|float $number;

    public function setNumber(int|float $number): void {
        $this->number = $number;
    }

    public function getNumber(): int|float {
        return $this->number;
    }
}

Supported Types

union型は、現在PHPでサポートされている全ての型をサポートしますが、一部の型については注意が必要です。

void型

void型は、union型の一部となることは決してできません。
T|voidのような型は、戻り値を含む全ての位置で不正です。

void型は関数に戻り値がないことを表します。
あらゆる非void型と互換がありません。

かわりにnull許容型?Tがあり、これはT型もしくはnullを返すことができます。

null許容型

T1|T2|nullとしてnullを許容するunion型を定義することができます。
既存の?Tは、T|nullの省略形と見做されます。

このRFCの草稿では、null許容型の文法が2種類になることを防ぐため、?(T1|T2)という文法を提唱していました。
しかしこの構文は都合が悪く、さらにphpdocで確立されているT1|T2|null構文とも異なっています。
議論の結果は、T1|T2|nullの文法が圧倒的に優勢でした。
?TT|nullの省略形として今後も有効な構文で、推奨も非推奨もされず、当分は廃止される予定もありません。

null型は、union型の一部としてのみ有効な型で、単独で使うことはできません。

union型と?T表記を混ぜて使用することはできません。
?T1|T2T1|?T2?(T1|T2)は全て不正な文法で、この場合はT1|T2|nullを使う必要があります。

false疑似型

現在では、エラーや不正が起きた際の関数の戻り値はnullにすることが推奨されていますが、歴史的理由から多くの内部関数はfalseを返してきます。
Statisticsセクションで示すように、union型を返す内部関数は大部分がfalseを含んでいます。

一般的な例としてはint|falseを返すstrposなどです。
これを正しく表記するとint|boolですが、これは関数がtrueを返すこともあるという誤った印象を与えます。

そのため、このRFCにはfalseのみを表すfalse疑似型が含まれています。
trueのみを表すtrue疑似型は、それが必要となる歴史的理由が存在しないため、このRFCには含まれません。

false疑似型は、union型の一部としてのみ有効で、単独やnull許容型として使うことはできません。
falsefalse|null?falseは全て無効な構文です。

Duplicate and redundant types

クラスのロードを行わずに検出できる冗長な記述は、コンパイルエラーになります。
これは以下のような例を含みます。

・同じ型の重複。int|string|INTは不可。
boolにはfalseを追加できない。
objectには個別のクラス型を追加できない。
iterableにはarrayTraversableを追加できない。

これは、型が最小であることを保証はしません。
たとえばクラスAとBがクラスエイリアスや継承関係にある場合、A|Bは有効です。

function foo(): int|INT {} // ×
function foo(): bool|false {} // ×

use A as B;
function foo(): A|B {} // × 構文解析時点でわかる

class_alias('X', 'Y');
function foo(): X|Y {} // 許可 実行時までわからない

Type grammar

特殊なvoid型を除くと、型の構文は以下のようになります。

type: simple_type
    | "?" simple_type
    | union_type
    ;

union_type: simple_type "|" simple_type
          | union_type "|" simple_type
          ;

simple_type: "false"          # union型でのみ有効
           | "null"           # union型でのみ有効
           | "bool"
           | "int"
           | "float"
           | "string"
           | "array"
           | "object"
           | "iterable"
           | "callable"       # プロパティ型指定では無効
           | "self"
           | "parent"
           | namespaced_name
           ;

Variance

union型は、既存の型ルールに従います。
・戻り値は共変。
・パラメータ型は反変。
・プロパティ型は不変。

唯一の変更点は、union型が派生型と相互作用する点であり、そのため3つの追加ルールがあります。

・全てのU_iV_jのサブタイプであった場合、U_1|...|U_nV_1|...|V_mのサブタイプである。
iterablearray|Traversableと同じと見做す。
・false疑似型はboolのサブタイプと見做す。

以下において、許可されているものと許可されないものの例を幾つか示します。

Property types

プロパティの型は不変です。
すなわち、継承しても型は同じである必要があります。
ただし、この"同じ"は意味が同じということを表します。
これまでもクラスエイリアスで同じクラスを表す別名を付けることができました。

union型はこの"同じ"の範囲を広げ、たとえばint|stringstring|intは同じとして扱います。

class A {}
class B extends A {}

class Test {
    public A|B $prop;
}
class Test2 extends Test {
    public A $prop;
}

この例では、親クラスのA|B型と子クラスのA型は明らかに異なる型であるにもかかわらず、これは正当な文法です。
内部的には、以下のようにこの結果に到達します。
まず、それはAのサブタイプであるため、AA|Bのサブタイプです。1
次にAAのサブタイプであり、BAのサブタイプであるため、A|BAのサブタイプです。

Adding and removing union types

戻り値からunion型の一部を削除し、パラメータに一部の型を追加することは正しい文法です。

class Test {
    public function param1(int $param) {}
    public function param2(int|float $param) {}

    public function return1(): int|float {}
    public function return2(): int {}
}

class Test2 extends Test {
    public function param1(int|float $param) {} // OK: パラメータの型追加は許可
    public function param2(int $param) {}       // NG: パラメータの型削除は禁止

    public function return1(): int {}           // OK: 返り値の型削除は許可
    public function return2(): int|float {}     // NG: 返り値の型追加は禁止
}

Variance of individual union members

同様に、戻り値の型を狭めたり、パラメータの型を広げることは許可されます。

class A {}
class B extends A {}

class Test {
    public function param1(B|string $param) {}
    public function param2(A|string $param) {}

    public function return1(): A|string {}
    public function return2(): B|string {}
}

class Test2 extends Test {
    public function param1(A|string $param) {} // OK: BをAに広げた
    public function param2(B|string $param) {} // NG: AをBに狭めた

    public function return1(): B|string {}     // OK: AをBに狭めた
    public function return2(): A|string {}     // NG: BをAに広げた
}

もちろん同じことを複数のunion型に同時に行ったり、型の追加削除と型の拡縮を組み合わせることもできます。

Coercive typing mode

strict_typesが有効でない場合、スカラー型宣言は暗黙の型変換の対象となります。
これは一部のunion型において、変更先の型を一意に決められないため問題となります。
たとえばint|stringfalseを渡すと、0""の両方が暗黙の型変換の候補になります。

従って、引数に正しくないスカラー値が渡ってきた場合、以下の優先順位で変換することにします。

1.int
2.float
3.string
4.bool

PHPの既存の型変換機構で型変換が可能である場合、その型が選ばれます。

例外として、値が文字列であり、union型がint|floatである場合、優先される型は引数の数値文字列の中身によって決まります。
すなわち、"42"はint型となり、"42.0"はfloat型となります。

上記のリストに含まれない型には自動型変換されません。
特にnullfalseへの自動型変換は起きないことに注意しましょう。

// int|string
42    --> 42          // 正しい型
"42"  --> "42"        // 正しい型
new ObjectWithToString --> "__toString()の結果" // objectはint型にならない
42.0  --> 42          // floatはint型になる
42.1  --> 42          // floatはint型になる
1e100 --> "1.0E+100"  // floatの上限を超えたらstring型になる
INF   --> "INF"       // floatの上限を超えたらstring型になる
true  --> 1           // boolはint型になる
[]    --> TypeError   // 配列はint|string型にならない

// int|float|bool
"45"    --> 45        // 整数っぽいのでint型になる
"45.0"  --> 45.0      // 小数っぽいのでfloat型になる
"45X"   --> 45 + Notice: Non well formed numeric string // 有効部分は整数っぽいのでint型になり、E_NOTICEが出る
""      --> false     // 数値形式文字列でないのでbool型になる
"X"     --> true      // 数値形式文字列でないのでbool型になる
[]      --> TypeError // 配列はint|float|bool型にならない

Alternatives

自動型変換については、別案が2種類ありました。

ひとつめはunion型は常に厳密な型指定とすることで、複雑な強制型変換を完全に排除することです。
これは2つの欠点があります。
まず、strictでないときに型をfloatからfloat|intにすると、直感に反して有効な入力が減ります。
第二に、float型float|int型のサブタイプと言えなくなるため、union型のモデルが崩壊します。

二つ目が、変換の優先順位を型の順番にすることです。
これは即ちint|stringstring|intが異なる型になることを意味します。
この変換は直感的ではなく、継承関係に非常に不明瞭な影響を及ぼします。

Property types and references

union型プロパティへの参照は、プロパティ型指定RFCに書かれた挙動に従います。
プロパティ型指定とunion型の組み合わせによる影響は、当時から考慮されていました

class Test {
    public int|string $x;
    public float|string $y;
}
$test = new Test;
$r = "foobar";
$test->x =& $r;
$test->y =& $r;

// $rと$test->xと$test->yは同じもので、型は{ mixed, int|string, float|string }の論理積になる

$r = 42; // TypeError

複数のリファレンスが紐付けられている場合、型の強制変換が行われた後の最終的な型は全ての型と互換する必要があります。
上記例の場合、$test->xはint型の42になり、$test->yはfloat型の42.0になります。
これは同じ型ではないため、TypeErrorが投げられます。

この場合は共通の型であるstring型にキャストすることでエラーは出なくなりますが、自動型変換による優先順位と異なるため、型がどうなるかわからないという欠点があります。

Reflection

union型をサポートするReflectionUnionTypeクラスが追加されます。

class ReflectionUnionType extends ReflectionType {
    /** @return ReflectionType[] */
    public function getTypes();

    /* Inherited from ReflectionType */
    /** @return bool */
    public function allowsNull();

    /* Inherited from ReflectionType */
    /** @return string */
    public function __toString();
}

getTypes()メソッドは、ReflectionTypeクラスの配列を返します。
この型は、元の型宣言と順番が異なる可能性があり、また等価である別の型になる可能性があります。

たとえばint|string型が["string", "int"]の順で要素を返す場合があります。
またiterable|array|string型は["iterable", "string"]になるかもしれないし["Traversable", "array", "string"]になるかもしれません。
Reflection APIが保証するのは、論理的に同じものであるということです。

allowsNull()メソッドは、union型の要素にnull型が含まれるか否かを返します。

__toString()メソッドは、型宣言を有効なコード表現として返します。
元の型宣言と必ずしも同じではありません。

後方互換性のため、null許容型?TT|nullのunion型は、ReflectionUnionTypeではなくReflectionNamedTypeを返します。

    // getTypes()や__toString()の結果は順番が異なることもある

    function test(): float|int {}
    $rt = (new ReflectionFunction('test'))->getReturnType();
    var_dump(get_class($rt));    // "ReflectionUnionType"
    var_dump($rt->allowsNull()); // false
    var_dump($rt->getTypes());   // [ReflectionType("int"), ReflectionType("float")]
    var_dump((string) $rt);      // "int|float"

    function test2(): float|int|null {}
    $rt = (new ReflectionFunction('test2'))->getReturnType();
    var_dump(get_class($rt));    // "ReflectionUnionType"
    var_dump($rt->allowsNull()); // true
    var_dump($rt->getTypes());   // [ReflectionType("int"), ReflectionType("float"), ReflectionType("null")]
    var_dump((string) $rt); // "int|float|null"

    function test3(): int|null {}
    $rt = (new ReflectionFunction('test3'))->getReturnType();
    var_dump(get_class($rt));    // "ReflectionNamedType"
    var_dump($rt->allowsNull()); // true
    var_dump($rt->getName());    // "int"
    var_dump((string) $rt);      // "?int"

Backwards Incompatible Changes

このRFCには、後方互換性のない変更はありません。
ただしReflectionTypeを利用しているコードは、union型に関する処理を追加する必要があります。

Future Scope

この項目は今後の展望であり、このRFCには含まれていません。

Intersection Types

交差型は論理的にunion型と似ています。
union型では少なくとも一つの型が満たされる必要がありますが、交差型では全ての型が満たされる必要があります。

たとえばTraversable|CountableTraversableCountableのうち少なくともどちらかである必要がありますが、Traversable&CountableTraversableでありなおかつCountableである必要があります。

Mixed Type

mixed型は、任意の値が受け入れ可能であることを表します。
型指定が無い場合と見た目の動きは同じですが、型指定が無いと、それが本当に自由であるのか、単に型指定を書き忘れただけなのかが区別できません。

Literal Types

このRFCで導入されたfalse疑似型は、TypeScriptでサポートされているリテラル型の特殊なケースです。
リテラル型は、列挙型のように一部特定の値のみを許可できる型です。

type ArrayFilterFlags = 0|ARRAY_FILTER_USE_KEY|ARRAY_FILTER_USE_BOTH;
array_filter(array $array, callable $callback, ArrayFilterFlags $flag): array;

列挙型ではなくリテラル型を使用する利点は、元の文法をそのまま維持できることです。
そのため、後方互換性を壊すことなく後付けすることができます。

Type Aliases

型が複雑になると、型宣言の再利用が必要になります。
その一般的な方法は2種類が考えられます。
ひとつめは次のようなエイリアスです。

use int|float as number;

function foo(number $x) {}

このnumber型はソースコード上でのみ現れる型で、コンパイル時に元のint|floatに解決されます。

もうひとつは型宣言を定義することです。

namespace Foo;
type number = int|float;

// \Foo\numberをどこからでも使える

Statistics

上位2000パッケージの@param@returnにおいて、野生のunion型がどれだけ使われているかを分析しました。

@paramのunion型:25k。一覧
@returnのunion型:14k。一覧

PHPの内部関数を調べたところ(調査が不完全なので最低2倍はあるはず)

・336関数がunion型を返す。
・そのうち213関数がfalse型を返す。

多くの内部関数が、戻り値の型を表すためにfalse疑似型が必要であることを示しています。

感想

元々型に厳密なTypeScriptがunion型を導入する理由はまあわかるんですよ。
しかしですね、元々フリーダム型だったPHPがわざわざ型宣言やらプロパティ型指定やらで狭めてきた上でのunion型って、なんというかこうマッチポンプ感とかそんな感じを感じざるをえない。
いやまあ違う話だってのは理屈ではわかるんですけど感覚的にね。

PHPにおいてこの機能の使い道は、DateTime|falsearray|nullといった、失敗時に例外を出さず無効値を返す関数の呼び出しにほぼ限られるでしょう。
あとは$_REQUESTを受け取るときのint|stringくらいでしょうか。
PDO|GMPなんて書いてきた人がいたら、設計をどうにかしろと突っ返します。

PHPでunion型をうまく使う方法、正直あまり思いつきません。
誰かがきっといいサンプルを考えてくれるはず。


  1. itがどれにかかってるのかわからなかった。というかここの文の意味がわからん。 

【Laravel5.8】Eagerロード先テーブルは必ず主キーもSELECTしないといけない

$
0
0

テーブルBと、B.idにリレーションを張ってるテーブルAがあったとします。

モデルは普通。

モデル
// モデルA
class TableA extends Model{
    // テーブル名
    protected $table = 'table_a';

    /**
     * リレーション
     * @return BelongsTo
     */
    public function tableb()
    {
        return $this->belongsTo(TableB::class, 'b_id', 'id');
    }
}

// モデルB
class TableB extends Model{
    // テーブル名
    protected $table = 'table_b';
}

コントローラとテンプレも普通。

コントローラ
    $tableas = TableA->get();
blade
@foreach ($tableas as $tablea)
    {{ $tablea->name }}
    {{ $tablea->tableb['name'] }}
@endforeach

さくっとできました。

できたはいいけどn+1問題が直撃する書き方です。
ループのたびにSELECT * FROM table_b WHERE id=xというSQLが走るので、とてもよろしくありません。
よってEagerローディングするようにしましょう。
コントローラにwith書くだけなので一瞬です。

コントローラ
    $tableas = TableA->with('table_b')->get();

何十個も発行されていたSQLがわずか二つに減りました。

しかし、これもまだ無駄があります。
テーブルAもBもnameしか使ってないので、SELECT *ではなくSELECT nameのSQLを発行するようにしたいです。
このためにLaravelにはスコープという機能があります。

モデル
// モデルA
class TableA extends Model{
    // テーブル名
    protected $table = 'table_a';

    /**
     * リレーション
     * @return BelongsTo
     */
    public function tableb()
    {
        return $this->belongsTo(TableB::class, 'b_id', 'id');
    }

    /**
     * 取得対象カラム
     */
    public static function scopeName($query){
        return $query->select(['name']);
    }
}

// モデルB
class TableB extends Model{
    // テーブル名
    protected $table = 'table_b';

    /**
     * 取得対象カラム
     */
    public static function scopeName($query){
        return $query->select(['name']);
    }
}

コントローラからはスコープの呼び出しを追加します。

コントローラ
    $tableas = TableA::name()->with(['tableb' => function ($q) {$q->name();}])->get();

やったね。

はい、これテーブルBのデータ取って来れません。
リレーション先テーブルのデータに主キーがなかった場合、何故か紐付けてくれないのです。

実はEagerローディングは予想に反してJOINしていません。
発行されたSQLを見ると、SELECT name FROM table_a WHERE id IN (1, 2, 3); SELECT name FROM table_b WHERE id IN (4, 5, 6);みたいになっています。
別々に取ってきたデータを後でくっつけているので、くっつけるために主キーが必要になっています。

マニュアルにも特定カラムのEagerロードという項目で一応触れられているのですが、スコープとはだいぶ離れたところにあるうえにwith('author:id,name')という例示のせいで一見同じものに見えないため、関係ないと読み流してしまいました。

ということできちんと結合するには、主キーも取ってくるようにしましょう。

モデル
// モデルB
    public static function scopeName($query){
        return $query->select(['id', 'name']);
    }

まとめ

SELECT先を指定するときは、必ず主キーも入れよう。

2020年のフロントエンドマスターになりたければこの9プロジェクトを作れ

$
0
0

以下はSimon Holdorf( dev.to/ Twitter / GitHub )による記事、9 Projects you can do to become a Frontend Master in 2020の日本語訳です。

9 Projects you can do to become a Frontend Master in 2020

Introduction

あなたがプログラミングの初心者であるか、既に経験豊富な開発者であるかにかかわらず、この業界では、急速な変化に追いつくために新しい概念と言語・フレームワークを学び続けることが必要です。
たとえばFacebookが4年前にオープンソース化したReactは、既に世界中のJavaScript開発者にとって第一の選択肢になっています。
もちろんVueとAngularにも多くのフォロアーがついています。
さらにはSvelte、Next.jsやNuxt.jsのようなユニバーサルフレームワーク、GatsbyにGridsomeにQuasarといった次なる者たちが出番を待っています。
JavaScript開発者として輝いていくためには、古いJavaScript以外にも、これら様々なフレームワークやライブラリもある程度は経験しておかなければなりません。

2020年のフロントエンドマスターになるために、ここに九つのプロジェクトを用意しました。
それぞれが異なる課題、異なるフレームワークやライブラリを備えていて、あなたのポートフォリオに追加されるべき技術スタックの糧となります。
実際に手を動かして物を作らければ何の助けにもならないことを覚えておいてください。
心を研ぎ澄ませ、実現させましょう!

Build a movie search app using React (with hooks)

React+フックで映画検索アプリを作成する。

最初に行うことは、Reactを使って映画検索アプリを作成することです。
以下は完成したアプリの外観になります。
01.jpg

What you will learn

比較的新しめのHooks API技術を使ってこのアプリを構築することで、Reactのスキルが向上します。
サンプルプロジェクトではReactコンポーネント、たくさんのフック、外部API、そしてもちろんCSSを使ったスタイリングを駆使しています。

Tech Stack & Features

・ReactおよびHooks
・create-react-app
・JSX
・CSS

クラスを使わずにこのプロジェクトを完成させることが、関数型Reactアプリの完璧な登竜門となり、2020年のプログラミングには間違いなく役立ちます。
サンプルアプリはhttps://www.freecodecamp.org/news/how-to-build-a-movie-search-app-using-react-hooks-24eb72ddfaf7/にあります。
チュートリアルに従ってもいいし、独自のフレーバーを追加するのもよいでしょう。

Build a chat app with Vue

Vueでチャットアプリを作成する。

もうひとつの要として、私のお気に入りでもあるJavaScriptのライブラリ、Vue.jsを使ってチャットアプリを作りましょう。
外観は以下のようになります。
02.png

What you will learn

以下のチュートリアルに従い、Vueアプリをゼロからセットアップし、コンポーネントを作成し、stateをハンドリングし、サードパーティのサービスに接続し、そして認証を行う方法を学びます。

Tech Stack & Features

・Vue
・Vuex
・Vue Router
・Vue CLI
・Pusher
・CSS

このプロジェクトは、Vueを使い始めるか、あるいは2020年に向けてスキルを向上させるにはもってこいのプロジェクトです。
チュートリアルはhttps://www.sitepoint.com/pusher-vue-real-time-chat-app/にあります。

Build a beautiful weather app with Angular 8

Angular 8でお天気アプリを作成する。

GoogleのAngular 8を使って、美しいお天気アプリを構築しましょう。
03.png

What you will learn

このプロジェクトでは、アプリをゼロから開発する際に必要となる貴重なスキル、設計から開発、プロダクションまでの一連の流れを学習できるでしょう。

Tech Stack & Features

・Angular 8
・Firebase
・Server-Side Rendering
・CSSグリッドレイアウトおよびFlexbox
・モバイルフレンドリー&レスポンシブ
・Dark Mode
・美しいUI

このプロジェクトにおいて私が本当に気に入っていることは、それぞれを単独で学ぶのではなく、設計から最終段階までの開発プロセスを通して学べるという点です。
本当に手を付けておくべきプロジェクトです。
https://medium.com/@hamedbaatour/build-a-real-world-beautiful-web-app-with-angular-6-a-to-z-ultimate-guide-2018-part-i-e121dd1d55e

Build a to-do app with Svelte

SvelteでToDoアプリを作成する。

SvelteはReactやVue、Angularと比べてまだまだ若輩ですが、2020年を嘱望されている期待の新星です。
To-Doアプリはもはやホットなトピックではありませんが、これはSvelteのスキルを磨くことに役立ちます。
04.png

What you will learn

このチュートリアルでは、Svelte3を使ってアプリを最初から最後まで作成する方法を学びます。
コンポーネント、スタイリング、イベントハンドラを利用します。

Tech Stack & Features

・Svelte 3
・Components
・Styling via CSS
・ES6

Svelteの良いチュートリアルはあまり多くないのですが、これは優れたスタータープロジェクトだと思います。
https://medium.com/codingthesmartway-com-blog/building-a-svelte-3-todo-app-from-start-to-deployment-1737f72c23a6
そして来年、このチュートリアルのより優れたバージョンを作成しているのはあなたかもしれません。

Build an e-commerce shopping cart with Next.js

Next.jsでショッピングカートを作成する。

Next.jsはReactのサーバサイドレンダリングを行う最も一般的なフレームワークです。
このプロジェクトは、次のようなEコマースショッピングカートを作成する方法を示します。
05.png

What you will learn

Next.jsの開発環境をセットアップし、新たなページとコンポーネントを作成し、データを取得し、アプリをデプロイする方法を学びます。

Tech Stack & Features

・Next.js
・コンポーネントとpages
・データ取得
・デプロイ
・SSRとSPA

新しいことを学ぶために、ショッピングカートなどの現実的な例を使うのはとても良いことです。
https://medium.com/@hamedbaatour/build-a-real-world-beautiful-web-app-with-angular-6-a-to-z-ultimate-guide-2018-part-i-e121dd1d55e

Build a full blown multi-language blog website Nuxt.js

Nuxt.jsで多言語対応ブログを作成する。

Nuxt.jsはVue版のNext.jsです。
サーバサイドレンダリングとSPAのいいとこ取りをした素晴らしいフレームワークです。
最終的な画面は以下のようになります。
06.jpg

What you will learn

このサンプルプロジェクトでは、初期設定から最終デプロイまでNuxt.jsを使ってWebサイトを構築する方法を解説しています。
Nuxt.jsの持つ多くのクールな機能を活かして、SCSSによるスタイリング、ページとコンポーネントを学びましょう。

Tech Stack & Features

・Nuxt.js
・コンポーネントとpages
・Storyblokモジュール
・Mixin
・Vuex
・SCSS
・Nuxtミドルウェア

あこれはあなたにぴったりのクールなプロジェクトであり、Nuxt.jsの優れた機能の多くをカバーしています。
私は個人的にNuxt.jsが好きなので、あなたも是非これを試してみて優れたVue開発者になってみてください。
https://www.storyblok.com/tp/nuxt-js-multilanguage-website-tutorial

Build a Blog with Gatsby

Gatsbyでブログを作成する。

Gatsbyは、内部的にReactとGraphQLを使っている優れた静的サイトジェネレータです。
以下がこのプロジェクトの完成形です。
07.png

What you will learn

このチュートリアルでは、Gatsbyを活用し、独自の記事を書くために使用できる優れたブログを構築する方法を学習します。

Tech Stack & Features

・Gatsby
・React
・GraphQL
・プラグインとテーマ
・MDX
・Bootstrap CSS
・テンプレート

ブログを作りたいと思ったのであれば、ReactとGraphQLを使うこのアプリはよいサンプルです。
Wordpressが常に悪いと言っているわけではありませんが、Gatsbyを使用すると、Reactを使いながらもパフォーマンスの高いサイトを作成できます。
https://blog.bitsrc.io/how-to-build-a-blog-with-gatsby-and-boostrap-d1270212b3dc

Build a Blog with Gridsome

Gridsomeでブログを作成する。

GridsomeはVue用の…まあその、NextとNuxtのような関係です。
要するにVue用のGatsbyです。
どちらもデータレイヤにはGraphQLを使いますが、フロントにはVueを使います。
ブログを作成するのに役立つ素晴らしい静的サイトジェネレータです。
08.png

What you will learn

このプロジェクトではGridsome、GraphQL、そしてMarkdownを使ってシンプルなブログを作る方法を学びます。
またNetlifyを使ってアプリをデプロイする方法も解説します。

Tech Stack & Features

・Gridsome
・Vue
・GraphQL
・Markdown
・Netlify

これはたしかに包括的なチュートリアルではないのですが、GridsomeとMarkdownの基本概念をカバーしており、学習の良い出発点になります。
https://www.telerik.com/blogs/building-a-blog-with-vue-and-markdown-using-gridsome

Build a SoundCloud-like audio player app with Quasar

QuasarでSoundCloudのようなアプリを作成する。

Quasarはモバイルアプリケーションの構築にも使用できる、Vueフレームワークです。
このプロジェクトでは、以下のようなオーディオプレイヤーアプリを作成します。
09.jpg

What you will learn

他のプロジェクトは主にWebアプリケーションに焦点を当てていましたが、このプロジェクトでは、VueアプリをQuasarを使ってモバイルアプリにする方法を学びます。
あなたのPCには、既にCordovaが動作するAndroidStudioかXCodeがインストールされていることでしょう。
そうでなければ、リンク先でセットアップする方法が示されています。

Tech Stack & Features

・Quasar
・Vue
・Cordova
・Wavesurfer
・UIコンポーネント

モバイルアプリを構築できるという、Quasarのパワーを示す小さなプロジェクトです。
https://www.learningsomethingnew.com/how-to-build-a-sound-cloud-like-audio-player-app-with-vue-js-quasar-and-wave-surfer

Conclusion

この記事では、手元でビルドできる9つのプロジェクトを紹介しました。
各プロジェクトは、それぞれ異なるJavaScriptフレームワークもしくはライブラリをメインに据えています。

これまで使ったことのない新たなフレームワークを使って、新しいことを試してみませんか?
それとも、既にある程度の知識を持っているテクノロジーを再学習し、スキルを強化しますか?
あるいは、全てのプロジェクトをお気に入りのフレームワーク/ライブラリで構築してしまいますか?
全てはあなたの選択次第です。

気軽にコメント、フォローください!

コメント欄

「ここ最近で最も役に立った記事のひとつ。」
「よい記事、多くの開発者がフレームワークを触ってみるのに役立つはず。」
「Nuxtの入口はwemake-vue-templateお勧め。フル機能のプロジェクトを簡単に始められる。」
「Gatxbyはテンプレート使う前にWebサイトのチュートリアルを読むといい。非常にわかりやすい。」
「バックエンドAPIは同じでフロントだけ色々なフレームワークでアプリ構築してる。よい比較になる。」
「JavaScriptのビギナーだけどやってみる。」「JavaScriptビギナーならバニラからやったほうがいい。」「何をしたいかによる。Vueが楽しいのであればVueから初めても何の問題も無い。」
「JavaScriptはとても勉強になった。けどCSSがよくわからん。units of measurementとか。」「Scott Tolinskiのサイトとかいいんじゃね。」「Jonas SchmedtmannのCSSコースがすごい助かった。」
「Webコンポーネントのお勧めプロジェクトとかある?」「なんか探してみるよ!」
「全部網羅するのはたいへんそう。Redux使ってるけど未だに状態管理に苦労してる。分野を集中した方がいいのでは?」「自分に合ったものを見つけられるように、意図的に広くしてる。」
「来週がんばる。」「今すぐやるんだよ!」
「emberがないよ」「いいプロジェクトがあったら追加するよ」「何も考えてなかったんでTwitterで聞いてみる」
「Aureliaは?ねえAureliaは??」

感想

なんたるマッチョ志向。
これだけ作れれば確かにフロントエンドマスターになれるだろうけど、それは単純にそれだけの力を付ける努力をしたからですね。
私はもっと楽してフロントエンドマスターになりたいんだよ。
いや、同じ給料が貰えるなら別にフロントエンドマスターにならなくてもいいんですけど。

そんなやる気の無い奴は置いておくとして、実際これだけのプロジェクトを完成させることができる人物であれば、2020年も最前線で活躍できることは間違いありません。
現在主流の技術、そして今後来るであろう技術をひととおり学べるので、年末に突然新パラダイムの技術が大ブレイクしたなんてことでもないかぎり当面は安泰でしょう。

【Laravel5.8】複数のデータベースに接続するときは全てのモデルに`$connection`を書こう

$
0
0

先日のEagerロード先テーブルは必ず主キーもSELECTしないといけないって記事を見るとわかりますが、Eagerロードでは複数テーブルを結合するのにJOINを使わず、データを別々に取ってきて後から合体させています。

つまり、これは異なるデータベースのテーブルに疑似リレーションを張れるのでは?

テーブルA
class TableA extends Model{
    // テーブル名
    protected $table = 'table_a';

    /**
     * リレーション
     * @return BelongsTo
     */
    public function tableb()
    {
        return $this->belongsTo(TableB::class, 'b_id', 'id');
    }
}
別DBのテーブルB
class TableB extends Model{
    // データベースがちがう
    protected $connection = 'other_database';

    // テーブル名
    protected $table = 'table_b';

    /**
     * リレーション
     * @return BelongsTo
     */
    public function tablea()
    {
        return $this->belongsTo(TableA::class, 'a_id', 'id');
    }
}

コントローラから呼び出す。

コントローラ
    $columnsAB = TableA->with('table_b')->get();
    $columnsBA = TableB->with('table_a')->get();

さて、この動作はどうなるでしょうか?

答えは、$columnsABは想定通りに取得できる、$columnsBAは取得できない、です。
わかりづら!

どうしてこんなことになるのでしょうか。

TableA→TableBの順で呼んだ場合、まずデフォルトのDBからTableAを呼び、次いでTableBを見に行った時点でDBが'other_database'に切り替わるのでTableBのデータも正常に取得できる、ということになります。

逆にTableB→TableAの順で呼んだ場合、TableBを見に行った時点でDBが'other_database'に切り替わってTableBのデータを正常に取得し、次いでTableAを見に行きますがDBは'other_database'のままでありTableAが読めない、となります。

原因はTableAに$connectionが書かれていないせいです。
普段$connectionを書いてないときは暗黙的にdefaultが選ばれるので、何も書かずとも問題なく動作します。
しかし一度でも別のデータベースが選ばれると、その後は$connectionを書かないかぎりそのDBが選ばれ続けてしまうみたいです。

まとめ

複数のデータベースに接続する可能性があるときは、デフォルト接続先にも必ず$connectionを書いておくと安全かもしれない。

Viewing all 337 articles
Browse latest View live