See the Elephant

1992生まれのプログラマが書くエンジニアブログ

【React】useEffectってなんだろ

useEffectってなんだろ

仕事でreactを読んでいた時に出てきた記法

useEffect(()=>{}, [])

副作用フックと呼ばれるらしい。広くはhooksと呼ばれるっぽいね

ja.reactjs.org

副作用フックは何かっていうと「コンポーネントの状態が変わった時にフックされる処理」って感じのイメージをしている。

useEffect(()=>{}, [])

これは何を表しているかというと

  • 第1引数 ()=>{} : フック時に実行される処理
  • 第2引数 [] : フックの監視対象とするstate項目

第2引数に何も指定されない場合は最初の一回だけ処理されるらしい。

ここまで理解すると以下のコードが読める。

useEffect(() => {
  document.title = `You clicked ${count} times`;
}, [count]);

count に副作用、つまり変更がある度に document.title が更新される。

react dom life cycle

公式曰く

React のライフサイクルに馴染みがある場合は、useEffect フックを componentDidMount と componentDidUpdate と componentWillUnmount がまとまったものだと考えることができます。

ふむ〜、componentDidMount とかはreactのdom生成ライフサイクルっぽいね。

素晴らしい図を書いている人がいたので引用

GitHub - iktakahiro/react-component-lifecycle-diagram

https://github.com/iktakahiro/react-component-lifecycle-diagram/raw/master/react-v16.2/react-component-lifecycle.png

  • componentDidMount: componentの描画を終えた
  • componentDidUpdate: componentのupdate描画を終えた
  • componentWillUnmount: componentがアンマウントされようとしている

多分こんなところだろう。いまはこれらを使うのは非推奨らしい。

簡単に無限ループを産むことができるから、と先輩から聞いた。

useEffectでreact dom life cycle も表現できる

qiita.com

から引用

componentDidMount

useEffect(() => {
    alert("最初のマウント時のみ実行される");
  }, []);

わかりやすいね。初期化処理に使われる。

componentDidUpdate

  useEffect(() => {
    alert("初回マウント時とstate更新後に毎回実行される");
  },[state]);

副作用が起きたら再描画。

これもわかりやすい。

あまりコンポーネントのライフサイクルを感じさせないのも個人的にはgood。

componentWillUnmount

  useEffect(() => {
    return () => {
      alert("アンマウント時のみ実行される");
    };
  }, []);

これはあんまり直感的ではないなー

高階関数にするとunmount相当になるらしい

なんでなんだろう。今回はそういうものって理解で収めておこう

何が嬉しいの?

一言で言うと、監視対象ごとに処理を切り分けられる からだと思う。

useEffect の第2引数で初期化や監視対象をコントロールできる。

// 初期化
useEffect(() => {
    document.user.name = "";
    document.user.email = "";
  }, []);


// ユーザ名
useEffect(() => {
    document.username = `You name is  ${props.user.name}`;
  }, [props.user.name]);

// email, tel
useEffect(() => {
    document.user.email = props.user.email;
    document.user.tel = props.user.tel;
  }, [props.user.email, props.user.tel]);

雑な例だが、監視対象ごとに処理を変更できる。

もう少し複雑な例では、入力されたデータに基づき加工したデータを表示したい場合は useEffectを利用すると対象がわかりやすく直感的に記述できる。

useEffect の嬉しさはそこだろう。

今日はここまで

【DDD】ドメイン駆動開発のメリットと始め方 codezineを読んでみる

【DDD】ドメイン駆動開発のメリットと始め方 codezineを読んでみる

codezine.jp

2ページまでよんだ。DDDってなんのために使うんだっけー?という話

DDDにおける2つの設計

戦略的設計と戦術的設計

whatとhowみたいな感じだな

  • 戦略的: 何を設計し実装するのか
  • 戦術的: どのように設計し実装するのか

「戦略的設計」を実施せず、エンジニアが取り組みやすい「戦術的設計」にだけ注力すると、「軽量DDD」と呼ばれる事業価値を発揮できない貧弱なDDDになってしまう

気をつけよ。ちゃんと要件を確認してモデリングしてからやりましょってことね

DDDの概略

DDDは「高品質」のソフトウェアを設計する手法です。ここでの高品質はバグのないという意味ではなく、ビジネス的にも成功していることを指します 顧客と開発者が共通言語(ユビキタス言語)で会話して、一体感あるチームとして、事業価値の高いソフトウェアを開発する手法

んーわかる。運用者と開発者、顧客が同じ単語で話すの大事

DDDのメリット

  • プログラマドメインエキスパート(業務に最も詳しい人)が共通言語で話していくことで顧客が開発するようにソフトウェアを作ることができる

  • ドメインエキスパートすら詳しくない業務を深く検討することでチーム内に業務知識が広まる

  • コードがドメインの設計を表すため翻訳が不要になる

  • DDDの戦略的設計を行うことは、開発費用をコストではなく事業を伸ばすための投資に転換できる。

fmfm. 確かに業務について深く考えることでビジネスにおけるコア要件の確定や不足箇所を洗い出すことができるよなー

ちゃんと要件相談して決めてから作ろうなっていう当たり前のことを言ってるんだけど、要件定義って時間かかるしここのコスト値切ろうとするのはわかるなーと。

値切った代償が大体、技術的負債になると思うしちゃんとやろうという戒め

複雑さへの対処

手続きを羅列するんじゃなくて、個々の役割をうまく果たすオブジェクトが協調しあって事を成すように作っていく。

個々の役割がドメインモデルで表現される と考えてこの先を読んでいこう。

ふと思いついたOOPとDDDの相性について

DDDとOOPの相性の良さは DDDでは業務をオブジェクトの振る舞いとして表現するドメインモデルとして実装する ことにあるよな。

OOPが扱うオブジェクトの方が担当領域が狭いけど、個々の役割をうまく果たすオブジェクトが協調しあって事を成すという点ではOOPとDDDは非常に相性がいいと思う。

2ページ読めたし、今朝はここまで。

このブログから生まれた友人との会話が良い感じだったので追記

f:id:namu_r21:20190726110551p:plain

【DDD】ドメインモデルとドメインサービスについて調べたがまだよくわかっていない

昨夜書いたブログにコメントが来た

sqlアンチパターン マジックビーンズに対するコメント

namu-r21.hatenablog.com

f:id:namu_r21:20190725233029p:plain

f:id:namu_r21:20190725233042p:plain

ちょこっと調べ、友人の言葉を借りた一旦の理解は以下。

正直よくわからないことがわかったって段階。

ドメインモデルとドメインサービスって何が違うんだ?

codezine.jp

ドメインサービス:エンティティや値オブジェクトの責務ではないドメインモデルのロジック(複数のドメインオブジェクトを使って計算する処理やファサード

この書き方からするに

ドメインモデルがドメインサービスを包含する ように見える

抜粋

 一般的な「サービス」という言葉には、複雑なビジネスロジックを使いやすい粒度にまとめたコンポーネントというイメージがあります。 DDDの「ドメインサービス」は従来の「サービス」が示すものとは異なります。「ドメインサービス」は粒度が粗いコンポーネントではなく、トランザクションの責務を担うわけでもありません。 ドメインサービスの役割は、ドメインモデルが扱う「粒度の細かい処理」を担うものです。その処理がエンティティ(5章)/値オブジェクト(6章)/集約(10章)でもない場合に、ドメインサービスとして実装します。そのため、ドメインサービスはユビキタス言語として表現されます。

んーわからん

ドメインモデルとは

ドメインモデルをまず考えたほうがよさそう

https://codezine.jp/article/detail/11221

「アプリケーション」とは、広義の意味では「システム」全体と同じ意味となりますが、本稿では、ドメインモデルを使用するクライアントである「ユーザーインターフェイス層」「アプリケーション層」について紹介します。

f:id:namu_r21:20190725233156p:plain

この図を見る限り

ドメインモデルがドメインサービスを包含する ように見える

は多分間違ってないな。

じゃ、アプリケーションサービスとドメインサービスは何が違うんだろう。

ここがはっきりすると「ドメイン」の意味する境界が見えそう。

アプリケーションサービス

非常に薄く、ドメインモデル上のタスクの調整に使うロジック(腐敗防止層の変換・アダプター等)

とのこと。つなぎ役、程度で理解しておくか。例えばcontrollerから呼び出されるロジックがこの辺りの話になるんだろうか?

https://www.ogis-ri.co.jp/otc/hiroba/technical/DDDEssence/chap3.html

●Anticorruption Layer(腐敗防止層)パターン 新規に構築するアプリケーションも、たいていはレガシーなど既存の外部システムと連携しなければならない。既存システムが持つドメインモデルは、これから構築しようとする新しいドメインモデルにはそぐわないことが多い。両者のモデルの対応付けが容易でない場合は、新システムと既存システムとの間に隔離層(腐敗防止層)を設けて、そこで両方向に対するモデルの変換を実装して両者のモデルを完全に独立させてしまう。ただし、腐敗防止層の構築には大きなコストがかかるので注意。

この表現から察するに、ビジネスクリティカルな知識は持たずデータのマッピングのような薄いロジックを担当するような層みたいだな。

アプリケーションサービスは、ある機能とある機能のつなぎ目、インタフェース、アダプターという単語で説明されるようだ。

と考えると、 アプリケーションサービスが知らないほとんどのビジネスロジックドメインモデルの中に閉じる ことになる。

https://codezine.jp/article/detail/10318?p=2

// 「ビジネス優先度を合計する」メソッド
    public BusinessPriorityTotals businessPriorityTotals(
            TenantId aTenantId,
            ProductId aProductId) {

        int totalBenefit = 0;
        int totalPenalty = 0;
        int totalCost = 0;
        int totalRisk = 0;

        // リポジトリからBacklogItemのコレクションを取得
        Collection<BacklogItem> outstandingBacklogItems =
                this.backlogItemRepository()
                    .allOutstandingProductBacklogItems(aTenantId, aProductId);

        // 複数のBaklogアイテムの集計処理を実施
        for (BacklogItem backlogItem : outstandingBacklogItems) {
            if (backlogItem.hasBusinessPriority()) {
                BusinessPriorityRatings ratings =
                        backlogItem.businessPriority().ratings();

                totalBenefit += ratings.benefit();
                totalPenalty += ratings.penalty();
                totalCost += ratings.cost();
                totalRisk += ratings.risk();
            }
        }

       // 戻り値であるBusinessPriorityTotalsの値オブジェクトを設定
        BusinessPriorityTotals businessPriorityTotals =
                new BusinessPriorityTotals(
                        totalBenefit,
                        totalPenalty,
                        totalBenefit + totalPenalty,
                        totalCost,
                        totalRisk);

        return businessPriorityTotals;
    }

コードを引用する。例えば、このような状態を持たず複数のドメインオブジェクトを組み合わせ、変換を行う処理をドメインサービスと呼ぶらしい。


ここまで調べてみてわかったが、まずDDDの前提知識である境界づけられたコンテキストや値オブジェクト、エンティティといった概念から順に抑えないとよくわからなさそう。

友人が言う言葉に補足して以下のような理解で今回はとどめておく。

【SQLアンチパターン】24章 マジックビーンズ

SQLアンチパターン】24章 マジックビーン

twadaさんが弊社でSQLアンチパターン勉強会をやってくださっている。

聴講メモと自分なりの解釈のまとめ。

マジックビーンズはMVCにおけるModelレイヤの振る舞いについて触れた章。

Active Recordパターンの扱い方について議論している。

結論

Active Recordパターンを使うときもビジネスロジックとDBの中間層(Repository層やドメインモデル層)を設けてあげることで辛さが薄くなる。

Active Recordパターンが登場する文脈

弊社エンジニアのブログも話の中で登場したので記載する: http://at-grandpa.hatenablog.jp/entry/2013/11/01/072636

ソフトウェアプロジェクトの最大のコスト要因は開発工数工数削減 = 開発コスト削減

重複部分をいかに早く作るか。つまりボイラープレートがあればいい。

再利用性を高めることで開発の生産性を改善できる。

webアプリの主流アーキテクチャ MVCRailsの関係性

Active Recordパターンの流行はRailsが立役者。

webアプリにおいて、もっとも有力なアーキテクチャMVC

なかでも、モデル(Model) はコントローラ、ビュー以外の全ての部分を担う補集合的な立場。

ビジネスロジックのほとんどを担う部分である。

つまり、Modelを単純化することができれば、アプリケーションそのものの開発コストも削減できる、という考え方が生まれた。

それをActive Record パターンで実現したのがRailsの思想。

active recordパターン

1. モデルオブジェクトのフィールドがテーブルの列と1:1対応する
2. モデルオブジェクトがテーブルへのcrud操作を知っている
3. モデル特有のビジネスロジックをもつ

これがアクティブレコードパターン。

MVCの役割

at-grandpaさんのブログから拝借

f:id:namu_r21:20190724192017p:plain

現在webアプリケーションではMVC2の方が主流。

  1. ユーザからの入力を受け取る
  2. コントローラがモデルを呼び出す
  3. モデルの状態を取得
  4. 取り出したモデルの状態をcontrollerが加工
  5. viewとして整形しuserに返す

元々は元祖MVCがオリジナルの考え方だった。

元祖の場合、ユーザからの入力(例えば文字入力)ごとにdbへのioが走り状態が変化しviewはそれを写像するだけ、という設計だった。

ネットワークを介してこの操作を行うことはいささか富豪的すぎたため取り入れられずMVC2の形が主流となった。

元祖MVCの考え方は現代のフロントサイドFWが取り入れている(fluxのような思想)

MVCにおけるModelの役割は曖昧

MVCにおいてVとCは役割が明白だが、モデルは役割が曖昧である。

開発者はソフトウェア設計の複雑さを低減するためにモデルの一般化を行いたい。

しかし、モデルを過度に単純化し、ただのデータアクセスオブジェクト(DAO)とみなしてしまうのが今回のアンチパターン

何が辛いのか

データベーススキーマ変更の影響をモロに受ける

ActiveRecordだとテーブルとクラス(モデルと呼ばれる)が1:1対応する。

モデルはテーブルと密結合なのでテーブルの変更が直で影響する。

ドメインモデル貧血症

モデルがビジネスロジックを持たない単なるデータオブジェクトとして扱うと、ビジネスロジックが複数のコントローラへ漏れ出してしまう。

つまりモデルの振る舞いの凝集度が下がる。

これが起きると何が辛いか。

1モデルのロジックが各所に点在してしまいメンテナンスがむずかしくなる。

そして、 テーブル変更がモデルへ影響を与え、モデルを利用しているコントローラのビジネスロジックにまで影響を与えてしまう のだ。

つまり、永続化層のリファクタリングをやりたいだけなのになぜかコントローラのレイヤまで影響を与えてしまいとても辛くなる。

モデルのテストが難しい

モデルがDBアクセスとビジネスロジック両方を保つため、ビジネスロジックのテストにDBが必要となる。

データベースへのIOを考慮したテストを組むことになりテスト困難になる。

しかも、コントローラにビジネスロジックが漏れ出していた場合、あるメソッドのテストをしたいだけなのにhttpを利用したE2Eテストが必要になる。

どの部分が問題を産みやすいのか見つけづらい上に、考慮すべき範囲が広い。

辛いことばかりに見える。

ではどうすればいいのか

railsにおけるモデルはデータアクセスオブジェクトとして機能する。

そこにビジネスロジックも乗っかっているような形になっている。

データアクセスの機能とビジネスロジックの機能を切り分けて考えればいい。

データアクセスはいわゆるRepository層的な役割として見立てモデルに任せる。

そしてビジネスロジックはモデルではない副作用を持たない別の純粋なクラスに置く(DDDのドメインモデル的な役割)。

こうすることでアプリケーションのビジネスロジックの関心事と永続化層の関心事を切り分けられる。

結論

Active Recordパターンを使うときもビジネスロジックとDBの中間層(Repository層やドメインモデル層)を設けてあげることで辛さが薄くなる。

【React-Redux】mapStateToPropsとmapDispatchToPropsについて学んでみる

mapStateToPropsとmapDispatchToPropsについて

昨夜はreduxについて学んだ。 今日はreact-reduxについて調べる。

https://namu-r21.hatenablog.com/entry/2019/07/22/192333

react-reduxについて

reactはdomレンダリングのためのFWである。 reduxはデータフロー制御のためのアーキテクチャである。

これら2つは直接的に関係しない。

というよりも、react上に描画されるデータのフロー制御をreduxに担当させる、という使われ方が多いだけである。

直接関係ないのだから、2つの世界を繋ぐインターフェースが必要となる。

これを行うために弊チームでは react-redux というライブラリを利用しているらしい。

詳しい話は公式に譲る。

https://github.com/reduxjs/react-redux

reactとreduxをconnectするのがreact-redux

https://react-redux.js.org/introduction/quick-start

クイックスタートから引用。

React Redux provides a connect function for you to connect your component to the store.

ほうほう。繋ぐと。

Normally, you’ll call connect in this way:

ん?

import { connect } from 'react-redux'
import { increment, decrement, reset } from './actionCreators'

// const Counter = ...

const mapStateToProps = (state /*, ownProps*/) => {
  return {
    counter: state.counter
  }
}

const mapDispatchToProps = { increment, decrement, reset }

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(Counter)

以上終わり。んんん???

mapStateToPropsmapDispatchToProps ってなんだよ。

公式を読んでみる mapStateToProps

参考にするコードはこれ

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(Counter)

https://react-redux.js.org/6.x/using-react-redux/connect-mapstate#connect-extracting-data-with-mapstatetoprops

As the first argument passed in to connect, mapStateToProps is used for selecting the part of the data from the store that the connected component needs.

connect関数の第一引数、mapStateToPropsは connected componentが必要とする形でstoreからデータの一部を選ぶために使われる。

- It is called every time the store state changes.
- It receives the entire store state, and should return an object of data this component needs.
  • storeのstateが変わるたびに呼ばれる
  • storeのstateを受け取り、componentが求めるデータObjectを返す

つまり、 storeのstateから必要なデータを抽出しcomponentに渡すための変換器 ってことっぽい。

connected component って多分reduxと接続された react componentのことだろう。上の例だと Counter がそれになりそう。

Counter に渡すpropsを定義するのが mapStateToProps の仕事っぽいね。

componentのためにpropsを作るmapStateToProps

https://react-redux.js.org/6.x/using-react-redux/connect-mapstate#return

以下のコードのようにstateの中身を元に新しいObjectを作りcomponentに渡す。

function mapStateToProps(state) {
  return {
    a: 42,
    todos: state.todos,
    filter: state.visibilityFilter
  }
}

// component will receive: props.a, props.todos, and props.filter

このようにしてstateからcomponentが必要なデータ(props)を作る(mapping)する。

だから mapStateToProps なんだね。

第2引数で state以外の値を取ることができるので、stateに新しい値を織り交ぜてpropsを作ることもできるらしい。

function mapStateToProps(state, ownProps?)

なんとなく理解できたから次に行こう

mapDispatchToProps を見てみよう

そもそも dispatch ってなんだっけ?

昨日学んだ記事から引用

dispatch: reducerが作ったstateを実際に適用し、storeで扱うstateを更新する

そうそう…storeの中にあるstateは外からは直接変更できなくて、 dispatch することでstateの更新ができるんだった。

dispatch という名前的に action を扱うような気がするよね。

見ていこう。

公式を読んでみる mapDispatchToProps

https://react-redux.js.org/6.x/using-react-redux/connect-mapdispatch#connect-dispatching-actions-with-mapdispatchtoprops

As the second argument passed in to connect, mapDispatchToProps is used for dispatching actions to the store.

dispatch is a function of the Redux store. You call store.dispatch to dispatch an action. This is the only way to trigger a state change.
  • mapDispatchToPropsを使うことでactionを元にしたstateの更新ができる(dispatch)。
  • dispatchはstateを変更できる唯一の方法である。

fmfm

With React Redux, your components never access the store directly - connect does it for you. React Redux gives you two ways to let components dispatch actions:

reduxでは componentからstoreに直接触れることはできないし、connectでも同じ。react-reduxでは2通りのdispatch方法があるよ。

By default, a connected component receives props.dispatch and can dispatch actions itself.
connect can accept an argument called mapDispatchToProps, which lets you create functions that dispatch when called, and pass those functions as props to your component.
  • props.dispatch を受け取ってdispatch(これが普通)
  • connect関数が受け取る mapDispatchToPropsによる方法。 -これはdispatchされた時に実行される関数を作ることができる上に、propとしてそれらの関数がcomponentに渡される。

ここまで読むとなんとなく以下までわかった

  • react componentにstoreを更新するための操作方法を提示するためのもの
  • どうやら dispatchの方法をcomponentに定義して渡せるっぽい

mapDispatchToProps の使われ方

mapDispatchToPropsを定義しない時はデフォルトのdispatchが使用される。

connect(mapStateToProps /** no second argument */)(MyComponent)

んー続きを読んでみたけどいまいちわからない。

ちょっと疲れてきたので今日はここまで

仕事に必要なのでreduxに入門した

reduxに入門した

仕事でreact-reduxの知識が必要なので学ぶ

1-2年前にflux勉強したので、それを思い出しながら。

namu-r21.hatenablog.com

flux理解してるしredux余裕やろ とか思ってたけど、まぁまぁむずいね。

今日の教科書はこれ。

qiita.com

reduxはイベントドリブンな状態操作を行う一方方向のデータフローだと思えば良さそう

ものすごくいい図だったので元記事から引用させていただく。

https://files.slack.com/files-pri/T2YT9EHKL-FLA7964LT/image.png f:id:namu_r21:20190722192435p:plain

action

action creatorはactionを作る actionはただのjson。データを変更するために送る信号。

{
  type: 'ADD_TODO',
  text: 'BBBBBBBBB'
}

state

stateはコンポーネントの状態。UIで扱うデータのまとまり。

実際はでっかいJsonみたいなイメージをすればわかりやすい。

stateをもとにcomponentを操作する。

reduxはstateを一方方向でデータ変更するデータフローアーキテクチャだと思えばわかりやすい。

store

storeがでっかいJsonを書き換え続けているようなイメージ。

storeの中にstateが保持されていて、ある決められた手順でstateの状態を書き換える。

reducer

reducer(state, action) => newState

と考えればとても分かりやすい

ようするにデータの状態を変更することにしか関心のない関数だ。

reducerはstateとactionをもとにstateを新規作成して返す。

function addTODO(state, action) {
  if (action.type != ADD_TODO) {
    return newState;
  }
}

それぞれの役割を考えてみよう

  • action: eventでありstateを変えるための信号
  • reducer: 信号とstateを受け取り新しいstateを作る
  • dispatch: reducerが作ったstateを実際に適用し、storeで扱うstateを更新する
  • store: stateを操作するためのインタフェース(reducer, dispatcher)を提供する&stateの操作を外部から隠蔽する

stateの変更操作はstoreの中に隠蔽されていて、storeの外から変更はできない(stateはstore外部ではimmutableなオブジェクトなため)。

外部から見えるのはstateそのものの状態(immutable)とstate変更用の信号(action)とstoreの信号受け口なんだな。

storeの中にデータ変更の操作方法(reducer)が定義されていて、変更結果を適用する(dispatch)と、storeの中のstateが書き換わる。

stateの変更操作のカプセル化と一方向のデータフロー制御。

ただそれだけの話だった。

実際に使うときはもう少し複雑っぽいので、都度、記事に起こそう

scala コレクションライブラリ foldLeft / foldRight

scala コレクションライブラリ foldLeft / foldRight

https://dwango.github.io/scala_text/collection.html#foldleft%EF%BC%9A%E5%B7%A6%E3%81%8B%E3%82%89%E3%81%AE%E7%95%B3%E3%81%BF%E8%BE%BC%E3%81%BF

foldLeft

foldLeft はリストの要素を左から順にfを使って「畳み込む」。

リストの要素に対して左側から関数をする、関数の結果と次の要素を取り出し関数を適用する、という風に再帰的に処理が繰り返される。

fold = 畳み込む という意味である。

codeで表すと以下のようになる

def foldLeft[B](z: B)(f: (B, A) ⇒ B): B

foldLeftはリストの左側からfを適用していく。zは初期値だ。

リストに加算を行う式は以下だ

scala> List(1, 2, 3).foldLeft(0)((x, y) => x + y)
res0: Int = 6

foldLeft が行う処理

List(1, 2, 3).foldLeft(0)((x, y) => x + y)

上記の式が計算する順序は以下だ。初期値を0としてListの先頭から加算を繰り返す。そしてリストの要素を一つの要素にたたみ込んで行く。

       +
      / \
     +   3
    / \
   +   2
  / \
 0   1

演習問題

foldLeftを用いて、Listの要素を反転させる次のシグニチャを持ったメソッドreverseを実装してみましょう:

def reverse[T](list: List[T]): List[T] = ???

コンスを使えばイケそう

scala> def reverse[T](list: List[T]): List[T] = {
     |   val rev: List[T] = Nil;
     |   list.foldLeft(rev)((r, x) => x :: r);
     | }
reverse: [T](list: List[T])List[T]

scala> List(1,2,3,4,5).reverse
res4: List[Int] = List(5, 4, 3, 2, 1)

なるほど。 1 :: Nil みたいにコレクション型を後置しないといけないんだね

foldRight

これはそのまま、右からの畳み込み。

codeで表現すると次になる。

def foldRight[B](z: B)(op: (A, B) ⇒ B): B

初期値とリストの右から要素を取り出し、関数を適用。その結果と1つ左の要素を取り出し関数を適用、という風に順番にたたみ込んで行く。

   +
  / \
 1   +   
    / \
   2   +   
      / \
     3   0

foldLeftが理解できているとわかりやすいね

練習問題1

Listの全ての要素を足し合わせるメソッドsumをfoldRightを用いて実装してみましょう。sumの宣言は次のようになります。なお、Listが空のときは0を返してみましょう。

def sum(list: List[Int]): Int = ???

自分の答え

これは簡単だね

scala> def sum(list: List[Int]): Int = {
     |   if (list.isEmpty) { return 0; }
     |   list.foldRight[Int](0)((x, y) => x+y);
     | }
sum: (list: List[Int])Int

scala> List(1,2,3,4,5).sum
res3: Int = 15

模範解答

def sum(list: List[Int]): Int = list.foldRight(0){(x, y) => x + y}

え、まじか。戻り値デフォルトで0なのね。これはfoldRightの仕様っぽいね

次の問題。

練習問題2

Listの全ての要素を掛け合わせるメソッドmulをfoldRightを用いて実装してみましょう。mulの宣言は次のようになります。なお、Listが空のときは1を返してみましょう。

def mul(list: List[Int]): Int = ???

自分の答え

これは模範解答通りだった。嬉しい。

scala> def mul(list: List[Int]): Int = list.foldRight(1)((x,y) => x*y);
mul: (list: List[Int])Int

scala> mul(List(1,2,3,4,5))
res4: Int = 120

練習問題3

mkStringを実装してみましょう。mkStringそのものを使ってはいけませんが、foldLeftやfoldRightなどのListに定義されている他のメソッドは自由に使って構いません。ListのAPIリファレンス を読めば必要なメソッドが載っています。実装するmkStringの宣言は

def mkString[T](list: List[T])(sep: String): String = ???

となります。残りの2つのバージョンのmkStringは実装しなくても構いません。

さて、急にレベル上がったな。

mkString はList内の要素を結合する関数だったはず。

自分の答え

あんまり綺麗な答えではないな。

scala> def mkString[T](list: List[T])(sep: String): String = {
     |   list.foldLeft("")((x, y) => { 
     |     if (x.isEmpty) {x + y.toString}
     |     else {x + sep + y.toString}
     |   });
     | }
mkString: [T](list: List[T])(sep: String)String

scala> mkString[Int](List(1,2,3))(",")
res13: String = 1,2,3

模範解答

確かに綺麗だけどまだmatch教えてくれてないやん!!!! ありかよ!!w

def mkString[T](list: List[T])(sep: String): String = list match {
  case Nil => ""
  case x::xs => xs.foldLeft(x.toString){(x, y) => x + sep + y}
}

2個目のcaseが気になった。

x::xs => xs.foldLeft(x.toString){(x, y) => x + sep + y}

x::xsなんらかのlistが2つ連結したもの と見立てることができる。

そう考えると x = Nil, xs = 処理したいList と考えてもいい。

scala> Nil :: List(1,2,3)
res14: List[Any] = List(List(), 1, 2, 3)

こう見ると xs には処理対象としたい list が入ってくるのがわかりやすい。

ということで今回はここまで。