プロが教える店舗&オフィスのセキュリティ対策術

Schemeのプログラミング うるう年関連
Schemeで、
「(1)閏年かどうかを判定する関数leap?(number -> boolean)を定義した後、(2)○年○月の日数は何日かを求める関数num-of-days(number number -> number)を定義せよ」
という内容の課題を出されたのですが、(例えば 2009年の7月→31日 2012年の2月→29日)
どうもエラーが出て実行できません。
自分は以下のように組みました。
;;(1)の関数
(define (leap? year)
(cond
[(= (remainder year 400) 0) #t]
[(and (= (remainder year 4) 0)(> (remainder year 100) 0)) #t]
[else #f]
)
)
;;(2)の関数
(define (num-of-days year month)
(cond
[(and (= month 2)(= (leap? year) #t)) 29]
[(and (= month 2)(= (leap? year) #f)) 28]
[(or (= month 1)(= month 3)(= month 5)
(= month 7)(= month 8)(= month 10)
(= month 12)) 31]
[else 30]
)
)

これを例えば
(num-of-days 2008 4) や (num-of-days 1995 12)
などとして実行すると、それぞれ30,31という正しい値を返してくれるのですが、
(num-of-days 2008 2) や (num-of-days 1995 2)
など、閏年・非閏年に関係無く、2月が絡むと
=: expects type <number> as 1st argument, given: false; other arguments were: true
というエラーを吐いてしまいます。
何度も見直しましたが、どこが間違っているのか見付けきれません…。どなたか間違いを指摘して頂けると幸いです。

A 回答 (3件)

ああ、ちなみに。



manaka3161 さんの作ったコードのロジックは「無駄がある」とは思いますが、基本的に「間違ってはいない」、とは思いますよ。
どこが悪いのか?と言うと、単純に、ここです。

(define (num-of-days year month)
(cond
[(and (= month 2)(= (leap? year) #t)) 29] ;; ここが間違ってる
[(and (= month 2)(= (leap? year) #f)) 28] ;; ここが間違ってる
[(or (= month 1)(= month 3)(= month 5)
(= month 7)(= month 8)(= month 10)
(= month 12)) 31]
[else 30]
)
)

「ロジックが合ってる」のに「エラーを返す」場合があるんです。何故なら、データ型に整合性がない、からです。
比較演算子=は「数値専用」で等価判定を下す。引数が数値である、ってのが前提なんですけど、引数にシンボル#tか#fが来てる、ってのがエラーの原因です。

> (= 1 2)
#f
> (= 1 1)
#t
> (= 'a 'b)
=: expects type <number> as 1st argument, given: a; other arguments were: b

=== context ===
/usr/lib/plt/collects/scheme/private/misc.ss:74:7

>

同じエラーですよね。見て分かる通り、=は数値しか引数に取れないんです。

つまり、シンボル同士を比較したい場合はeq?使った方が良いでしょう。すなわち、

(define (num-of-days year month)
(cond
[(and (= month 2)(eq? (leap? year) #t)) 29] ;二番目の判定式を eq? で行う
[(and (= month 2)(eq? (leap? year) #f)) 28] ;二番目の判定式を eq? で行う
[(or (= month 1)(= month 3)(= month 5)
(= month 7)(= month 8)(= month 10)
(= month 12)) 31]
[else 30]
)
)

これで動く筈です。

> (num-of-days 2009 7)
31
> (num-of-days 2012 2)
29
>

慣れない内は、数値/シンボルの比較を両方「考えずに行いたい」のなら、eqv?を使った方が良いかもしれません。eqv?はリストじゃなければ数値/シンボルの両方とも行えます。eq?はシンボル専用、ですね。=は数値専用。
大まかに言うと、繰り返しになりますが、次のようになってます。

eq? => シンボル比較の時に使う。高速。
eqv? => 数値、シンボル両方O.K。ある意味デフォルト。
equal? => リスト同士まで比較出来る。ただし、eq?に比べると遅い。
= => 数値専用。
    • good
    • 0
この回答へのお礼

eq? => シンボル比較の時に使う。高速。
eqv? => 数値、シンボル両方O.K。ある意味デフォルト。
equal? => リスト同士まで比較出来る。ただし、eq?に比べると遅い。
= => 数値専用。

↑まさに私が求めていた解説でした!すごく分かり易くて、助かりました!
eq?を使用すれば、回りくどいけれども、あくまで自分の考えた内容でも通すことが出来るのですね。本当に有り難うございました。

お礼日時:2010/04/27 20:58

;;; #t とか #f が返り値の場合、and や or 使った方がシンプル


(define (leap? year)
 (and (zero? (modulo year 4))
    (or (not (zero? (modulo year 100)))
      (zero? (modulo year 400)))))

;;; こう言う場合、cond を使うとメンド臭いんで、case を使った方がシンプルに書けると思う
(define (num-of-days year month)
 (case month
  ((1 3 5 7 8 10 12) 31)
  ((2) (if (leap? year) 29 28))
  (else 30)))
    • good
    • 0
この回答へのお礼

caseはDrSchemeで認識してくれませんでした…。ですがifを使って補助関数を作成することで解決しました。有り難うございました。

お礼日時:2010/04/27 20:51

(= (leap? year) #t) のところでしょうね. boolean を返しているんだから, 「何かと比較する」必要などな

いのでは?

この回答への補足

申し訳ありません。このleap?関数で求めた"真" "偽"をどうnum-of-days関数に組み込むのかが分からず、感覚的にやった部分です…。
(1)でtrueだった場合は28を返し、falseだった場合は29を返すようにするには、具体的にどう記述すれば良いのでしょうか?

補足日時:2010/04/27 19:50
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!