質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
Scala

ScalaはJava仮想マシンで動作を行うオブジェクト指向型プログラミング言語の1つです。静的型付けの関数型言語で、コンパイルエラーの検出に強みがあります。

Haskell

Haskellは高い機能性をもった関数型プログラミング言語で、他の手続き型プログラミング言語では難しいとされている関数でも容易に行うことができます。強い静的型付け、遅延評価などに対応しています。

Node.js

Node.jsとはGoogleのV8 JavaScriptエンジンを使用しているサーバーサイドのイベント駆動型プログラムです。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

関数型プログラミング

関数型プログラミングとは、関数を用いて演算子を構築し、算出し、コンピュータプログラムを構成する枠組みです。

Q&A

解決済

2回答

4192閲覧

この関数は参照透過性があるといえますか?

退会済みユーザー

退会済みユーザー

総合スコア0

Scala

ScalaはJava仮想マシンで動作を行うオブジェクト指向型プログラミング言語の1つです。静的型付けの関数型言語で、コンパイルエラーの検出に強みがあります。

Haskell

Haskellは高い機能性をもった関数型プログラミング言語で、他の手続き型プログラミング言語では難しいとされている関数でも容易に行うことができます。強い静的型付け、遅延評価などに対応しています。

Node.js

Node.jsとはGoogleのV8 JavaScriptエンジンを使用しているサーバーサイドのイベント駆動型プログラムです。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

関数型プログラミング

関数型プログラミングとは、関数を用いて演算子を構築し、算出し、コンピュータプログラムを構成する枠組みです。

0グッド

0クリップ

投稿2017/10/09 08:43

副作用が関数内に入ってるコード

JavaScript

1const age = (birthYear) => { 2 3 const today = new Date(); 4 const thisYear = today.getFullYear(); 5 return thisYear - birthYear; 6}

副作用から分離されたコード

JavaScript

1const age = (birthYear, thisYear) => { 2 return thisYear - birthYear;

使う側

JavaScript

1age(2000, (new Date()).getFullYear());

「関数型プログラミングの基礎」という書籍の2章でこのようなコードがあったのですが、
副作用から分離されたコードと言っても、結局、外部の「日付」というコンテキストに処理結果が依存してくるので、
果たしてこれは参照透過性があるコードと言って良いものなのかどうかわからず混乱してしまいました。

どう考えると良いのでしょうか?

一応、本では副作用を持つ部分と持たない部分を分離して、副作用がない方の関数だけでも参照透過性をもたせようぜってことだったのですが、それに渡す引数がDateオブジェクトの日付みたいなものだと参照透過性があるって言えるのかわかりません。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

例として挙げられている副作用から分離されたコードは、たしかに参照透過性があります。参照透過性とは、その式をその式の値に置き換えても、プログラムの観測可能な振る舞いが変わらないことを指します。別の言い方をすると、参照透過性のある関数は、同じ入力に対して、同じ作用と同じ出力を持ちます。

たとえばこのように使った場合、age()は毎回同じ値を返します。たとえプログラムの実行中に年が変わっても、age()の返す値は変わりません。

javascript

1const thisYear = new Date().getFullYear(); 2 3age(2000, thisYear); 4age(2000, thisYear); 5age(2000, thisYear);

一方、new Date()にはご指摘の通り参照透過性がありません。なにも引数を与えてないのに実行した時点によってそれが保持する値(日付)が変わります。

javascript

1new Date(); 2new Date(); 3new Date();

参照透過性のある関数はテストが容易に行えますので、そのような関数の割合を増やしていくことで、バグの少ないプログラムを実現できます。

投稿2017/10/09 10:46

編集2017/10/09 10:54
tatsuya6502

総合スコア2035

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2017/10/09 11:16

ご回答ありがとうございます。サンプルのコードは「age(2000, (new Date()).getFullYear());」のように、引数でnew Date()を使っているからややこしいのですかね。。。ご掲示頂いたコードのように先にconstに現時点での年を文字列で格納してそれをage関数の引数に使うのであれば、たしかにいつ実行しても同じですね。いや、、、同じじゃない???const thisYearに入る値が実行する年によって変わってしまいますよね?thisSecondみたいにわかりやすくすると、それがより顕著になりそうな?
tatsuya6502

2017/10/09 11:40 編集

もちろん、同じプログラムを来年実行したら、今年実行した時と振る舞いが変わります。しかし、その理由はage()への入力が2017から2018に変わったためであって、age()に参照透過性がないためではありません。age()は参照透過性の条件「同じ入力に対して、同じ作用と同じ出力を持つ」は満たしています。違う入力を与えたら違う出力をするのは、ある意味あたり前と言えるでしょう。 JavaScriptについては詳しくないのですが、テストを書く時のことを想定するとわかりやすいかもしれません。たとえば仮にテストをassert_eq(期待値, 実際の値)の形式で書くとしたら、参照透過性のあるage()ではassert_eq(17, age(2010, 2017)); assert_eq(18, age(2010, 2018));というようにテストできます。 一方で参照透過性のないage()ではテストをどう書いたらいいでしょうか? もしassert_eq(17, age(2010)) と書いた場合、今年はテストにパスしますが、来年はパスしなくなってしまいます。このように入力以外の何かの要因によって出力が変わる関数には参照透過性がありません。
退会済みユーザー

退会済みユーザー

2017/10/09 11:47

なるほど!そのように考えるとしっくり来ますね!ありがとうございました!参考になりました!
guest

0

副作用から分離されたコードと言っても、結局、外部の「日付」というコンテキストに処理結果が依存してくるので、

果たしてこれは参照透過性があるコードと言って良いものなのかどうかわからず混乱してしまいました。

実際のシステムは時間や状況に応じて容易に変動する入力と出力にあふれている。
ファイルの入出力、日付、ユーザーの入力値…etc

要するに副作用無しのシステムというのは考えられないわけだね。
じゃあどうするねんって話なんだけど、
参照透過性があるかつ副作用のないコードの領域を出来る限り広げる」事が目的。

JavaScript

1const age = (birthYear, thisYear) => thisYear - birthYear; 2age(2000, (new Date()).getFullYear());

なので、上記コードは全体で見れば副作用があるけど、
その根幹になっている計算ロジックthisYear - birthYearを参照透過性があるかつ副作用のない関数で逃して死守することに成功したわけだね。

だからage関数は別ファイルに分けて、様々な数値や境界値チェックを試みたりして正しく動作することを容易に証明出来るってわけ。


以下蛇足

より実践的にするなら誕生日が例えば10月5日だとすれば、
今日が10月4日か10月6日かでも結果が変わって当然だから、実践的にはMoment.jsを使ってこんな感じになりそうだね。

javaScript

1// age :: Moment -> Moment -> Number 2const age = (birth, today) => today.diff(birth, 'years'); 3 4console.log(moment().format()) // "2017-10-10T12:14:26+09:00" 5console.log(age(moment('2000-10-09'), moment())) // 17 6console.log(age(moment('2000-10-11'), moment())) // 16

投稿2017/10/10 03:17

miyabi-sun

総合スコア21158

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問