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

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

新規登録して質問してみよう
ただいま回答率
85.35%
アルゴリズム

アルゴリズムとは、定められた目的を達成するために、プログラムの理論的な動作を定義するものです。

ソート

複数のデータを、順序性に従って並べ替えること。 データ処理を行う際に頻繁に用いられ、多くのアルゴリズムが存在します。速度、容量、複雑さなどに違いがあり、高速性に特化したものにクイックソートがあります。

再帰

情報工学における再帰とは、プログラムのあるメソッドの処理上で自身のメソッドが再び呼び出されている処理の事をいいます。

OCaml

OCaml(オーキャムル)は、フランスのINRIAが開発した関数型言語MLの一種で、 最新の言語理論の成果が取り入れられているプログラミング言語です。

Q&A

解決済

1回答

1162閲覧

ocamlの同一キーの合成

grape_ll

総合スコア83

アルゴリズム

アルゴリズムとは、定められた目的を達成するために、プログラムの理論的な動作を定義するものです。

ソート

複数のデータを、順序性に従って並べ替えること。 データ処理を行う際に頻繁に用いられ、多くのアルゴリズムが存在します。速度、容量、複雑さなどに違いがあり、高速性に特化したものにクイックソートがあります。

再帰

情報工学における再帰とは、プログラムのあるメソッドの処理上で自身のメソッドが再び呼び出されている処理の事をいいます。

OCaml

OCaml(オーキャムル)は、フランスのINRIAが開発した関数型言語MLの一種で、 最新の言語理論の成果が取り入れられているプログラミング言語です。

0グッド

0クリップ

投稿2021/05/16 12:19

###やりたいこと
[("apple", 1); ("grape", 1) ; ("apple", 2); ("grape", 3); ("melon", 3); ("banana", 3)]
(=List)

というリストをキーが同じものをまとめて

[("apple", [1;2]) ; ("grape", [1;3]) ; ("melon", [3]) ; ("banana", [3])]
というリストにしたい

以下に思考の殴り書きのような形で,思いついたことを書かせていただきます.

###考えた方法

ocaml

1# let synthe pair1 pair2 = 2 if (fst (pair1)) = (fst (pair2)) then (fst (pair1), [snd (pair1) ; snd (pair2)]) 3 else (fst (pair1), [snd (pair1)]);; 4val trya : 'a * 'b -> 'a * 'b -> 'a * 'b list = <fun> 5# synthe ("app",1) ("app", 2);; 6- : string * int list = ("app", [1; 2]) 7# synthe ("app",1) ("apple", 2);; 8- : string * int list = ("app", [1])

h::restでListの先頭を取ってrest中の全要素について上記のsyntheを行い,
再帰でrestについてh::restとし,同様に行う
つまり

ocaml

1match List with 2 |[] -> [] 3 | h::rest -> (syntheで作ったもの)::(この関数名) (restなどの引数)

を改善したようにする.

###問題点
このままでは合成したものの残りかすが出現してしまう.
例えば
上の例では("apple", [1;2]) と ("apple", [2])の二つが出来てしまう
また,「h::restでListの先頭を取ってrest中の全要素について上記のsyntheを行い」の部分の具体的な実装が分からない

###考えた方法2
Listを辞書順にソートし,となり合ったものの第一項を見て一緒ならまとめる,異なるならば注目する組を移す

[("apple", 1); ("grape", 1) ; ("apple", 2); ("grape", 3); ("melon", 3); ("banana", 3)]
→[●("apple", 1); ("apple", 2) ;("grape", 1) ; ("grape", 3); ("melon", 3); ("banana", 3)]
→[●("apple", [1;2]) ;("grape", 1) ; ("grape", 3); ("melon", 3); ("banana", 3)]
→[("apple", [1;2]) ; ●("grape", [1]) ; ("grape", 3); ("melon", 3); ("banana", 3)]
...
→[("apple", [1;2]) ; ("grape", [1]) ; ("grape", [3]); ("melon", [3]); ●("banana", [3])]

のような動き(●は注目してる組)

###問題点2
シンプルなリストのソートは出来るが,組のリストのソートはList.sortでは無理だった.

###お聞きしたいこと
・上記の考え方の補強となる考え方
・同じことができる他のアルゴリズム

宜しくお願いいたします.

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

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

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

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

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

guest

回答1

0

ベストアンサー

「考えた方法2」で進めるのが良さそうに思いました。

シンプルなリストのソートは出来るが,組のリストのソートはList.sortでは無理だった.

手元で試してみたところList.sortでソートできました。compare関数を使います。

ocaml

1(* string * int型は比較できる *) 2# ("apple", 1) <= ("apple", 2);; 3- : bool = true 4 5# compare;; 6- : 'a -> 'a -> int = <fun> 7 8(* 左 < 右のときはマイナス値、左 = 右のときは0、左 > 右のときはプラス値を返す *) 9# compare ("apple", 1) ("apple", 2);; 10- : int = -1 11 12# compare ("apple", 1) ("banana", 1);; 13- : int = -1 14 15# let l = [("apple", 1); ("grape", 1) ; ("apple", 2); ("grape", 3); ("melon", 3); ("banana", 3)];; 16 17# List.sort compare l;; 18- : (string * int) list = 19[("apple", 1); ("apple", 2); ("banana", 3); ("grape", 1); ("grape", 3); ("melon", 3)]

となり合ったものの第一項を見て一緒ならまとめる,異なるならば注目する組を移す

こういう集計っぽいものが入っている処理は、多くの場合foldで処理できます。

たとえば以下のようにfold_leftで処理する場合、

List.fold_left (fun acc item -> ...) [] [("apple", 1); ("apple", 2); ("banana", 3); ...]

以下のように結果のリストを作り上げていきます。(accitemfold_leftに渡す匿名関数の引数。->はその関数からの戻り値で、次のaccになります)

ocaml

1acc: [] 2item: ("apple", 1) 3 -> [("apple", [1])] 4 5acc: [("apple", [1])] 6item: ("apple", 2) 7 -> [("apple", [2; 1])] 8 9acc: [("apple", [2; 1])] 10item: ("banana", 3) 11 -> [("banana", [3]); ("apple", [2; 1])]

匿名関数ではaccのheadとitemについてそれぞれの第一項を比較して、まとめるかどうかを判断します。

なお、上の例のようにfold_leftを使うと結果のリストの順番が逆転してしまいます。fold_rightを使うと逆転しないのですが、巨大なリストを処理しようとすると、再帰呼び出しの積み重なりが多すぎてスタックオーバーフローエラーになることがあります。事前にリストの長さが短いとわかっていない限りはfold_leftを使うのが無難です。

このfold_leftの逆転問題は、入力となるリストを逆順にソートしておくことで解決できます。以下のようにcompareの返す値の正負を逆転すると逆順にソートできます。

ocaml

1# List.sort (fun x y -> ~- (compare x y)) l;; 2- : (string * int) list = 3[("melon", 3); ("grape", 3); ("grape", 1); ("banana", 3); ("apple", 2); ("apple", 1)]

追記

コメント欄でLists.fold_leftを使わない方法で完成したとの連絡を受けました。完成してよかったです!

参考までにLists.fold_leftを使った方法を追記します。

ocaml

1# let l = [("apple", 1); ("grape", 1) ; ("apple", 2); ("grape", 3); ("melon", 3); ("banana", 3)];; 2val l : (string * int) list = 3 [("apple", 1); ("grape", 1); ("apple", 2); ("grape", 3); ("melon", 3); ("banana", 3)] 4 5# let l' = List.sort (fun x y -> ~- (compare x y)) l;; 6val l' : (string * int) list = 7 [("melon", 3); ("grape", 3); ("grape", 1); ("banana", 3); ("apple", 2); ("apple", 1)] 8 9# let f = fun acc item -> 10 match (item, acc) with 11 (* 第一項が一緒なのでまとめる *) 12 | ((x, y), (x', ys)::rest) when x = x' -> (x, y::ys)::rest 13 (* 第一項が異なる。(accが[]のときも含む) *) 14 | ((x, y), _) -> (x, [y])::acc;; 15val f : ('a * 'b list) list -> 'a * 'b -> ('a * 'b list) list = <fun> 16 17# List.fold_left f [] l';; 18- : (string * int list) list = 19[("apple", [1; 2]); ("banana", [3]); ("grape", [1; 3]); ("melon", [3])]

投稿2021/05/16 15:27

編集2021/05/18 13:43
tatsuya6502

総合スコア2046

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

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

grape_ll

2021/05/18 12:01

丁寧な解説ありがとうございます. 実装の点で1つ解決できない問題に直面したため,質問させていただきます. fold_leftを用いた実装が具体的に思い浮かばなかったので次の代案で処理しようと考えたのですが,エラーに対処することが出来ない状態です.修正箇所を教えていただけますでしょうか. #let listing list = List.map (fun (x,y) -> (x,[y])) list;; # let rec synthe list ans front = match list with | [] -> front::ans | h::rest -> if (snd (front)) = [0] then synthe rest ans h else if (fst (front) = fst (h)) then synthe rest ans (fst (h), (snd (h))@(snd(front))) else synthe rest front::ans h;; Error: This expression has type 'a * int list but an expression was expected of type ('a * int list) list The type variable 'a * int list occurs inside ('a * int list) list ソートした後にこれを行おうと思っています. 下線部は最後のfrontにひかれていました. 宜しくお願いいたします.
tatsuya6502

2021/05/18 13:14

最後の行ですが、エラーになっていた front::ans のところを () で囲んで、 synthe rest (front::ans) h としたところコンパイルできました。
grape_ll

2021/05/18 13:31

結びつきが弱いのを忘れていました,初歩的なミスですね,気を付けます. 完成しました.ありがとうございます.
tatsuya6502

2021/05/18 13:45

完成してよかったです! 回答にLists.fold_leftを使った例を追記しましたので、よかったら参考にしてください。
grape_ll

2021/05/21 02:45

ありがとうございます.そちらの方法も参照させていただきます.
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問