メールの詳細(トピック表示)
relatively prime
投稿者:Jun Mukaiさん 2004/01/08 19:56 MLNo.32 [メール表示]
こんにちは.向井と申します.
先日より,「互いに素」を計算するプログラムで頭を悩ませています.お知恵
を拝借したく,メールしました.
http://www.sampou.org/cgi-bin/cahier.cgi?Cahier%3a2004-01-02&l=jp
↑この URL で,真理値からなるテーブルで互いに素かどうか判断させる
Haskell のプログラムがあるのですが,同じことを OCaml でやろうとすると,
This kind of expression is not allowed as right-hand side of `let rec'
と言われてしまい,コンパイルできません.
この話から派生して,いくつか別アプローチがあり,
http://www.sampou.org/cgi-bin/cahier.cgi?comment%3aCahier%3a2004-01-02&l=jp
http://www.sampou.org/cgi-bin/cahier.cgi?comment%3aCahier%3a2004-01-03&l=jp
この2つは実装できました.
しかし,
http://www.sampou.org/cgi-bin/cahier.cgi?comment%3aCahier%3a2004-01-05&l=jp
の方は同じくエラーが出てコンパイルできなくなります.
このエラーメッセージは,なぜ出るのでしょうか? また, OCaml ではこの
ような記述をする方法はないのでしょうか?
私の書いたプログラムを付します.「遅延リスト」のモジュールを自作してい
るのでやや長いですが,肝心の定義じたいはそれほどでもないと思います.
--
module type LSEQ =
sig
type 'a seq;;
val empty : 'a seq;;
val is_empty : 'a seq -> bool;;
val of_list : 'a list -> 'a seq;;
val to_list : 'a seq -> 'a list;;
val of_a : 'a -> 'a seq;;
val cons : 'a -> 'a seq Lazy.t -> 'a seq;;
val append : 'a seq -> 'a seq -> 'a seq;;
val head : 'a seq -> 'a;;
val tail : 'a seq -> 'a seq;;
val take : int -> 'a seq -> 'a seq;;
val nth : int -> 'a seq -> 'a;;
val map : ('a -> 'b) -> 'a seq -> 'b seq;;
val cycle : 'a seq -> 'a seq;;
val ( @! ) : 'a seq -> int -> 'a;;
val ( @: ) : 'a -> 'a seq -> 'a seq;;
end
module Lseq : LSEQ =
struct
type 'a seq = Nil | Cons of 'a * ('a seq Lazy.t);;
exception Empty
let empty = Nil;;
let is_empty = function
| Nil -> true
| _ -> false;;
let rec of_list = function
| [] -> Nil
| x::xs -> Cons(x, lazy(of_list xs));;
let rec to_list = function
| Nil -> []
| Cons(h, t) -> h::to_list (Lazy.force t);;
let of_a a = Cons(a, lazy(empty))
let cons n s = Cons(n, s);;
let rec append s1 s2 = match s1, s2 with
| Nil, _ -> s2
| Cons(h1, t1), _ -> Cons(h1, lazy(append (Lazy.force t1) s2));;
let head = function
| Nil -> raise Empty
| Cons(h, _) -> h;;
let tail = function
| Nil -> raise Empty
| Cons(_, t) -> Lazy.force t;;
let rec take n = function
| Nil -> Nil
| Cons(h, t) ->
if n = 0 then Nil
else Cons(h, lazy(take (n-1) (Lazy.force t)));;
let rec nth n = function
| Nil -> raise Empty
| Cons(h, t) -> if n = 0 then h else nth (n-1) (Lazy.force t);;
let rec map f = function
| Nil -> Nil
| Cons(h, t) -> Cons(f h, lazy(map f (Lazy.force t)));;
let cycle s =
let rec ap = function
| Nil -> ap s
| Cons(h, t) -> Cons(h, lazy(ap (Lazy.force t)))
in ap s;;
let ( @! ) s n = nth n s;;
let ( @: ) e s = cons e (lazy s);;
end;;
open Lseq;;
let rec from n = cons n (lazy (from (n+1)));;
let natural = from 0;;
let rec col = function
| 0 -> (fun m -> false)
| 1 -> (fun m -> true)
| n -> (fun m -> (row n) (m mod n))
and row n = (fun m -> (col m) n);;
let rec s = function
| 0 -> false @: true @: (cycle (of_a false))
| n -> map (nth n) (map s (cycle (take n natural)));;
let rec ss = (false @: true @: (cycle (of_a false))) @:
(map (fun n -> map (nth n) (cycle (take n ss))) (from 1));;
let rec ss = map s natural
and s = function
| 0 -> cycle (of_a false)
| 1 -> cycle (of_a true)
| n -> cycle (map (nth n) (take n ss));;
読み込み中...-
MLNo.33
さん
(0) 2004/01/09 00:29 [メール表示する]

let rec には制限があります。基本的に関数定義以外で使うと結果は実装依存
になります。多分現状では、再帰定義される名前が、関数定義に含まれるか、
コンストラクタ ( :: とか)の引数になる場合だけ許されます。
http://ocaml.jp/archive/ocaml-manual-3.06-ja/manual015.html
From: Jun Mukai
Subject: [ocaml:0032] relatively prime
Date: Thu, 08 Jan 2004 19:56:33 +0900
> let rec ss = (false @: true @: (cycle (of_a false))) @:
> (map (fun n -> map (nth n) (cycle (take n ss))) (from 1));;
エラーになるのはここだと思うのですが、 lazy を明示的に使ってみたらどう
でしょうか。
それはそうと、
> let ( @: ) e s = cons e (lazy s);;
これでは s は lazy にはならないですよ。関数の引数はみな eager に評価さ
れますから。

-
MLNo.34
Jun Mukaiさん
(0) 2004/01/09 17:04 [メール表示する]

向井です.
山形さんありがとうございました.
> let rec には制限があります。基本的に関数定義以外で使うと結果は実装依存
> になります。多分現状では、再帰定義される名前が、関数定義に含まれるか、
> コンストラクタ ( :: とか)の引数になる場合だけ許されます。
> http://ocaml.jp/archive/ocaml-manual-3.06-ja/manual015.html
そうか,そうでした.って自分で訳したはずなのに忘れているのだから世話な
いですね.関数の引数に自身が含まれるとすると,たとえば
let rec a = a + 1;;
なる式も通ってしまうことになり,整合性が取れなくなるということなんでしょ
うかね.
> > let rec ss = (false @: true @: (cycle (of_a false))) @:
> > (map (fun n -> map (nth n) (cycle (take n ss))) (from 1));;
>
> エラーになるのはここだと思うのですが、 lazy を明示的に使ってみたらどう
> でしょうか。
エラーの場所を明示せず,すいませんでした.後ろの2つの ss がエラーにな
ります.
なるほど, lazy を使うことは考えていませんでした.そこでこの ss を,
let rec ss = lazy((false @: true @: (cycle (of_a false))) @:
(map (fun n -> map (nth n) (cycle (take n (Lazy.force ss))))
(from 1)));;
と書き換えてみたところ,エラーは出なくなりました.しかし,いざ
Lazy.force ss;;
とやると, Lazy.Undefined なる例外が発生します.
一方,後者の ss については次のように定義したところ,無事に定義でき,ま
た計算もできました.
let rec ss = lazy(map s natural)
and s = function
| 0 -> cycle (of_a false)
| 1 -> cycle (of_a true)
| n -> cycle (map (nth n) (take n (Lazy.force ss)));;
こちらは正しく計算できているようです.
ありがとうございました.
> それはそうと、
>
> > let ( @: ) e s = cons e (lazy s);;
>
> これでは s は lazy にはならないですよ。関数の引数はみな eager に評価さ
> れますから。
こちらは把握しています.この演算子は記述を容易にするために導入していて,
たとえば,[false; true; false] のようなリストを記述するときに,
false @: (lazy true @: (lazy false @: (lazy empty)))
などと書くのは煩雑なため,敢えて lazy を外す仕様にしています.
……といっても,他の人が見ることをあまり考えていなかったので,わかりに
くくなっているかもしれません.すいません.
それから,後で気付いたんですが演算子の結合性って最初の文字で決まるんで
すよね.だから,モジュール内の型に対して演算子を定義する場合には,プレ
フィクスで識別するのではなくて, +. のようにサフィックスで識別するべき
でした.
さらに追記ですが, camlp4 のストリームパーサを使えば,ストリームでかな
り自然に遅延リストっぽい挙動を扱えるんですね.これは知りませんでした.
いちいち無限リストを定義するより,こちらを使った方がいいのかもしれませ
ん…….

- MLNo.35 さん (0) 2004/01/09 23:11 [メール表示する]
-
MLNo.36
Jun Mukaiさん
(0) 2004/01/10 00:01 [メール表示する]

向井です.
> いま頭がぼーっとしているので原因は分りませんが、この例外は lazy
> value の値が自分自身の値に依存している時に起きます。
こんな感じでしょうか?
# let rec a = lazy(Lazy.force a);;
val a : 'a Lazy.t =
# Lazy.force a;;
Exception: Lazy.Undefined.
なるほど.となると,
let rec ss = lazy((false @: true @: (cycle (of_a false))) @:
(map (fun n -> map (nth n) (cycle (take n (Lazy.force ss))))
(from 1)));; ~~~~~~~~~~~~~
ここが問題になるわけですね.
おそらく,下線部の force で ss を展開しようとすると,そこで map を参照
して自身を展開しようとしてしまうために例外が発生するのでしょう.となれ
ば,この map の評価を lazy にすれば動くように思います.
let rec ss = lazy((lazy(false @: true @: (cycle (of_a false)))) @:
(map (fun n -> lazy(map (fun m -> nth n (Lazy.force m))
(cycle (take n (Lazy.force ss))))) (from 1)));;
かなりわけわからんと思いますが(lispみたい), ss は bool Lseq.seq
Lazy.t Lseq.seq Lazy.t 型になっています.
これで試してみると,
# #use "rprime.ml";;
# open Lseq;;
# nth 10 (nth 10 (Lazy.force ss));;
- : bool = false
# nth 11 (nth 10 (Lazy.force ss));;
- : bool = true
# nth 9 (nth 10 (Lazy.force ss));;
- : bool = true
# nth 8 (nth 10 (Lazy.force ss));;
- : bool = false
というように,ちゃんと動いているようです.
ありがとうございました.


