Building Abstractions with Functions - The Elements of Programming - Example: Square Roots by Newton's Method - conditional expressions, evaluation, order
Structure and Interpretation of Computer Programs: JavaScript Edition(Harold Abelson(著)、Gerald Jay Sussman(著)、Julie Sussman(著)、The MIT Press)のChapter 1(Building Abstractions with Functions)、1.1(The Elements of Programming)、1.1.7(Example: Square Roots by Newton’s Method)、Exercise 1.6の解答を求めてみる。
predicate ? then_clause : else_clauseの場合の評価はpredicateが真の場合にはthen_clauseが評価されて、偽の場合にはelse_clauseが評価されるけど、conditional(predicate, then_clause, else_clause)の場合には引数のpredicate、then_clause、else_clauseが全てが評価される。
なので、sqrt_iterの呼び出しの評価について、例えばguessが1、xが2の場合、
- sqrt_iter(1, 2)
- conditional(is_good_enough(1, 2), 1, sqrt_iter(improve(1, 2), 2))
- conditional(abs(square(1) - 2) < 0.001, 1, sqrt_iter(improve(1, 2), 2))
- conditional(abs(1 * 1 - 2) < 0.001, 1, sqrt_iter(improve(1, 2), 2))
- conditional(1 < 0.001, 1, sqrt_iter(improve(1, 2), 2))
- conditional(false, 1, sqrt_iter(average(1, 1/2), 2))
- conditional(false, 1, sqrt_iter(1.5, 2))
- conditional(false, 1, conditional(is_good_enough(1.5, 2), 1.5, sqrt_iter(improve(1.5, 2), 2)))
- …
となって終了しない。
実際に確認。
コード
let abs = Math.abs;
function square(x) {
return x * x;
}
function improve(guess, x) {
return average(guess, x / guess);
}
function average(x, y) {
return (x + y) / 2;
}
function is_good_enough(guess, x) {
return abs(square(guess) - x) < 0.001;
}
function conditional(predicate, then_clause, else_clause) {
return predicate ? then_clause : else_clause;
}
function sqrt_iter(guess, x) {
return conditional(
is_good_enough(guess, x),
guess,
sqrt_iter(
improve(guess, x),
x
)
);
}
console.log(sqrt_iter(1, 2));
入出力結果(Terminal, Zsh)
% node answer1.6.js
/Users/…/answer1.6.js:13
return abs(square(guess) - x) < 0.001;
^
RangeError: Maximum call stack size exceeded
at is_good_enough (/Users/…/answer1.6.js:13:5)
at sqrt_iter (/Users/…/answer1.6.js:20:9)
at sqrt_iter (/Users/…/answer1.6.js:22:9)
at sqrt_iter (/Users/…/answer1.6.js:22:9)
at sqrt_iter (/Users/…/answer1.6.js:22:9)
at sqrt_iter (/Users/…/answer1.6.js:22:9)
at sqrt_iter (/Users/…/answer1.6.js:22:9)
at sqrt_iter (/Users/…/answer1.6.js:22:9)
at sqrt_iter (/Users/…/answer1.6.js:22:9)
at sqrt_iter (/Users/…/answer1.6.js:22:9)
%
遅延評価のHaskellなら上手くいく。
コード
import Distribution.Simple.Utils (xargs)
square :: Num a => a -> a
square x = x * x
improve :: Fractional a => a -> a -> a
improve guess x = average guess $ x / guess
average :: Fractional a => a -> a -> a
average x y = (x + y) / 2
isGoodEnough :: (Ord a, Fractional a) => a -> a -> Bool
isGoodEnough guess x = abs (square guess - x) < 0.001
conditional :: Bool -> p -> p -> p
conditional predicate then_clause else_clause =
if predicate
then then_clause
else else_clause
sqrtIter :: (Ord t, Fractional t) => t -> t -> t
sqrtIter guess x =
conditional
(isGoodEnough guess x)
guess $
sqrtIter (improve guess x) x
main :: IO ()
main = do
print $ sqrtIter 1 2
入出力結果(Terminal, Zsh)
% runghc answer1.6.hs
1.4142156862745097
%