• 締切済み

Hanafuda Shuffle を関数型言語で。

関数型言語を学んだのですが、あまり概念がよくわかっておりません。 以下のような問題があるとき、関数型言語ではどのように解決すれば良いのでしょうか。 Hanafuda Shuffle http://www.deqnotes.net/acmicpc/1978/ja 考え方等だけでも大変参考になるのですが、可能であればHaskellやScalaで解いて頂きたいです。 宜しくお願いします。

みんなの回答

  • T_GYOUTEN
  • ベストアンサー率100% (1/1)
回答No.3

F#でごめんなさい。下を実行して問題文のように入力すると data = (5, 2, [(3, 1); (3, 1)]) ans = 4 data = (10, 3, [(1, 10); (10, 1); (8, 3)]) ans = 4 と表示されます。(なお文頭の半角スペースがうまく表示できないので、半角スペースの代わりに全角スペースを使ってます。) //(方針)カードの位置は一番下を1、一番上をnとする //逆写像を次々に施して位置n(一番上)にあるものが、どの位置に戻っていくかを調べる。最初の状態で位置と //カードの数字は同じなので、位置を答えれば答えとなる。 ///問題にあるような入力をしたとき[(5, 2, [(3, 1); (3, 1)]); (10, 3, [(1, 10); (10, 1); (8, 3)])]を返す関数 let inputData () =     let rec inputDataSub subNum subCountMax subCount  subLstAcc acc =         let oneLineIntsArr =              (System.Console.ReadLine()).Split([|' '|])                 |>Array.map(fun str -> System.Int32.Parse(str.Trim()))         match oneLineIntsArr with         |[|0;0|]     -> List.rev (List.tail ((subNum,subCountMax,subLstAcc)::acc)) //最後         |[|x;y|]  when subCount = 0 //グループの始まり                      -> inputDataSub  x  y 1 [] acc          |[|x;y|]  when subCount = subCountMax //グループの終わり                      -> inputDataSub  0 0 0 [] ((subNum,subCountMax,(List.rev ((x,y)::subLstAcc)))::acc)             |[|x;y|]     ->inputDataSub subNum subCountMax (subCount+1) ((x,y)::subLstAcc) acc //グループ途中         |_ -> failwith "input error"     inputDataSub 0 0 0 [] []  // getTopNum (5, 2, [(3, 1); (3, 1)])で4を返す関数 let getTopNum (n,shouffleCount,p_c_lst) =        let f_inv n (p,c)  = (fun x -> if x > n-c then x-p+1 elif x <= n-c-p then x else x + c)//逆写像         let applyFuncLst = List.map (f_inv n) (List.rev p_c_lst)         List.fold (fun pos g -> g pos) n applyFuncLst //場所nから逆写像でどんどん戻していく //「入力Dataと答えのタプル」のリスト let DataAndAnsLst =     inputData ()         |> List.map (fun ele -> (ele,getTopNum ele)) //表示         List.iter(fun (data,ans) ->printfn "data = %A ans = %A" data ans ) DataAndAnsLst

全文を見る
すると、全ての回答が全文表示されます。
  • dscripty
  • ベストアンサー率51% (166/325)
回答No.2

入門して 2週間後 1年半のブランク、全然かけない。とほほ。 とりあえず動くようにはなったけど、無駄が多くて参考になるかどうか。 $ cat | scala Shuffle 5 2 3 1 3 1 10 3 1 10 10 1 8 3 0 0 log => List(3, 5, 4, 2, 1) log => List(4, 3, 5, 2, 1) log => List(10, 9, 8, 7, 6, 5, 4, 3, 2, 1) log => List(1, 10, 9, 8, 7, 6, 5, 4, 3, 2) log => List(4, 3, 2, 1, 10, 9, 8, 7, 6, 5) 4 4 $ [Shuffle.scala] object Shuffle {   // 一回だけシャフルする関数   // desk:List[Int] ※ カードの山   // ds:(Int, Int) ※ データセット p が ds._1 で c が ds._2   def shuffle_once( deck:List[Int], ds:(Int, Int) ):List[Int] = {     val (topList, tailList) = deck.splitAt(ds._1-1)     val (middleList, bottomList) = tailList.splitAt(ds._2)     // 最後の式が返される     println("log => " + (middleList ::: topList ::: bottomList).toString) // 状態確認用     middleList ::: topList ::: bottomList   }   // データセットの数だけ再帰   def shuffle( deck:List[Int], ds:List[(Int, Int)] ):List[Int] = ds match {     case List() => deck     case _ => shuffle( shuffle_once(deck, ds.head), ds.tail )   }   // 入力データを分割して (カードの山, データセット) のリストを作成   // input:List[(Int, Int)] ※ 最後が (0, 0) のタプルで入力終了。そのあとは無視。   // 出力 ※ (List[Int], List[(Int, Int)]) shuffle 関数の入力   def devideDataSet(input:List[(Int, Int)]):List[(List[Int], List[(Int, Int)])] = input match {     case List() => List()     case head::tail => if ( (0, 0) == head ) List() else {       val (n, r) = head       val (ds, next) = tail.splitAt(r)       ((1 to n).reverse.toList, ds) :: devideDataSet(next)     }   }   // String を (Int, Int) へ変換、エラーは null を返す   def toIntInt(s:String):(Int, Int) = s.split(' ') match {     case Array(a, b) => try { (a.toInt, b.toInt) } catch { case _ => null }     case _ => null   }   // 標準入力を List[String] へ変換   def fromStdin():List[String] = readLine match {     case null => List()     case s => s::fromStdin()   }   def main(args: Array[String]) {     // 標準入力から読み込んで、異常な行は読み飛ばす     val input:List[(Int,Int)] = fromStdin().map(toIntInt(_)).filter{       case null => false       case _ => true     }     // 入力から読み込んだ List[(Int,Int)]     // → List((カードの山, データセット))     // → List(カードの山をデータセットの方法でシャフルした結果の一番上のカード)     // のそれぞれを出力     devideDataSet(input).map(el => shuffle(el._1,el._2).head).foreach(println(_))   } }

全文を見る
すると、全ての回答が全文表示されます。
  • hitomura
  • ベストアンサー率48% (325/664)
回答No.1

入力の解析と値のチェックは省略します。 以下をコンパイルして、 hanafuda_shuffle:exec([[(nの値), (rの値)], [(1番目のpの値), (1番目のcの値)], [(2番目のpの値), (2番目のcの値)], …, [((r - 1)番目のpの値), ((r - 1)番目のcの値)], [0, 0]]). と入力してやれば求める結果がでるはずです。 -module(hanafuda_shuffle). -export([exec/1]). shuffle(Pile, []) -> % (3) Pile; shuffle(Pile, [[0, 0] | _Rest]) -> % (4) Pile; shuffle(Pile, [[P, C] | Rest]) -> % (2) {First, Others} = lists:split(Pile, P - 1), {Second, Third} = lists:split(Others, C), shuffle(lists:append(Second, First, Third), Rest); exec([[N, R] | Shuffle]) -> [Top | _] = shuffle(lists:seq(N, 1, -1), Shuffle), % (1) Top. ただ、listsモジュールに依存していることが問題ですが……あ、Erlangは解いてほしい言語の指定にありませんでしたね。 (まじめな話、自分は関数型言語はErlangしか知らない) とりあえず、流れとしては (1):降順に並んだ山札を作成し(lists:seq(N, 1, -1))、それと並べ替え手順の列とでshuffle関数を呼び出します。 (2):並べ替え手順の列から先頭の手順を取り出し、その値によって山札をFirst, Second, Thirdの3つに分けます。そしてそれをSecond, First, Thirdの順に連結しなおして新たな山札とし、手順を取り出した残りの並べ替え手順の列とともにshuffle関数を呼び出します。 (ErlangではリストLの先頭の値を取り出す場合[T | R] = Lと書きます。Lの先頭の値がTに、Lから先頭を取り除いたリストがRに入ります) (3), (4):上記の処理を並べ替え手順の列が空になる(3)か手順が0, 0だった(4)ときまで行われます。このとき、その時点での山札がその時点での関数の戻り値になります。 (Erlangは関数内で最後に評価した値がその関数の戻り値となります。このばあい、ただ"Pile"と書いてあるので、ここでPileの値が評価され、それが関数の最後の評価なのでPileの値が戻り値となります) (2):shuffle関数を呼び出した結果をそのまま戻り値にします。 (1):shuffle関数から戻ってきた山札を先頭とそれ以外に分けます。そして、先頭の値を処理結果とします。 となります。 上記が参考になれば幸いです。lists:***のコードも書いてほしいと言うのであれば補足願います(処理の本質と関係ないので必要ないと思いますが)。

kojihiro1102
質問者

補足

ありがとうございます! ソースコードが簡潔に書かれていて美しいですね! おかげさまで大枠を掴むことができました。 ところで、関数型言語は変数の扱いが特殊と聞くのですが、 インプットした値をあとで使うように変数で保持することはできないのでしょうか。 可能ならば、題意の通り、 input 5 2 3 1 3 1 10 3 1 10 10 1 8 3 0 0 output 4 4 と出力してみたいのですが。 また、HaskellやScalaで解ける方がいらっしゃいましたら、是非参考にしてみたいです。 宜しくお願いします。

全文を見る
すると、全ての回答が全文表示されます。

関連するQ&A

  • 関数型言語の普及について

    関数型言語の普及について 趣味でプログラミングを勉強しているものです。今までにJavaやRuby等、オブジェクト指向言語を中心に勉強してきました。 今日、あるきっかけで関数型言語のHaskellを勉強し始めました。 そして、実際にプログラミングをしてみたり、関数型言語について調べてみると、まだ大きなアプリは書けないものの、今までのやり方(手続き指向、オブジェクト指向)が不要なのではないかと危惧する程の斬新さ、強力さが感じられました。 しかし、そんな関数型言語も未だに普及しているとは到底言えません。Haskellは関数型言語の中でも新しいもののようですが、それでもJava、Rubyよりも昔に発表されている言語です。 どうしてHaskell等の関数型言語は主流になっていないのか、関数型言語が従来の言語に劣っている点を中心に、皆さんの意見を聞かせてください。

  • お勧めの関数型言語はありますか?

    関数型言語を習得しようと思います。 お勧めのものを教えてください。 特徴、長所、短所なども教えていただければ幸いです。 私としてはHaskell、Lisp、Schemeがよいのではないかと 思っております。 よろしくお願いいたします。

  • 初めて関数型言語を学ぶとしたら、どの言語がお奨めですか?

    初めて関数型言語を学ぶとしたら、どの言語がお奨めですか? JavaScriptをやっていて、関数型言語に興味を持ちました。 いままで、勉強した言語はC < Java < Python < JavaScriptです。(右側の方が比重・興味が大きい) 現在、Web系志望の学生なので、その辺を踏まえてアドバイスいただけると助かります。 今のところ興味を持ってるのは、Common Lisp/Haskellあたりです。 よろしくお願いします。

  • lispとその他関数型言語について

    「lispを学べば悟りが開ける」という言葉をよく聞きます。 l他のプログラミング言語哲学とは一線を画すほどの教示をもった言語という印象を持ちます。 僕もlispを少し学んだだけですが「悟り」は開けませんでした。 しかし他の関数型言語(haskellとか)ではそういう話は聞きません。 なぜでしょうか。 やっぱ括弧ですか。

  • 関数型言語を独学で勉強している学生です

    情報系の大学3年生です。 僕は関数型言語に興味がありhaskellやlispを勉強しています。 しかしこれらの言語で何か作るのは結構しんどいと思います。 ぶっちゃけ、javaとかrubyとかpythonの方が作りやすいでしょう。 haskellは出力するにも一苦労だしlispはリストが面倒。 関数型言語は実用的だとは思えません。 しかし、楽しいです。 どう表現すればいいかわかりませんが、とにかく関数型言語は面白いです。 そこで質問です。 昨今のIT企業は新卒採用の際、学生時代に作ったプログラムを評価し採用の是非を決めると思います。 もし関数型言語で何か作っても評価されるのでしょうか。 僕ができることといえば、本やサイトに載ってあるサンプルを少し改良するぐらいです。 そんな作品を企業側が積極的に評価し、採用してくれるでしょうか。 それともこんな浮世離れしたことやるよりrubyとかpythonで奇抜なアイデアのプログラムを組んで、twitterやブログで奇を衒っていかにもギークっぽく振舞ったほうがいいんでしょうか。 文章がめちゃくちゃですみません。 とにかく僕はこのまま今の勉強を続けてもいいのか、それがわからないんです。 自分で考えるべきことでしょうが、調べるばかりで頭が混乱して日常生活に支障がでてきてます。 誰がアドバイスください。 よろしくおねがいします。

  • 【関数型言語,論理学】推論して関数を自動生成する

    こんにちは。 関数型言語(haskell)や論理学を独学している者です。 勉強中ふと思ったことがあるので質問します。(以降、表記はhaskell文法に倣います) 例えば今、我々に与えられた関数は (x -> Int)型の関数fと、(Int -> y) 型の関数gと((b -> c) -> (a -> b) -> a -> c)型の関数(.)だけだとします(a,b,c,x,yは全て型変数)。それ以外の関数は存在しません。 この時、(x -> y) 型の関数hは例えば(g . f)と表せると思います。 Int=b, x=a, y=cとみなせば、hは簡単に作れます。 しかし、それはあくまで人間にとって簡単だということです。 これを「計算機が作る」ことは可能でしょうか。 つまり、与えられた関数(と型の情報)だけで特定の型の関数を自動生成できるプログラムは存在し得るか、ということです。 カリー=ハワード同型対応という性質がありますね。これは簡単に言うと「ある型を持つプログラム(関数)が一つでも書ければその型に対応した命題は真」ということだと思いますが、僕が聞きたいのは「その命題(型)が真かどうか分からないけど、前提は用意するので証明(プログラム)は計算機に任せてもいいのか」ということです。 CoqやPrologという、計算機で証明を行うプログラミング言語があるというのは知っていますが勉強したことが無いのでよくわかりません。 よろしくお願いします。

  • C言語の関数を分かりやすく説明してください。

    C言語を学習しています。 C言語の入門書を読んでいて、どうしても関数のこと(引数とか戻り値とか自作関数についてのこと)が分かりませんでした。ネットで調べてみたりもしたのですが分かりませんでした。 かみ砕いて説明していただけるとありがたいです。

  • C言語の関数について困っています

    こんにちは。 C言語で、テキストファイルの行数を数える関数があれば教えて頂けないでしょうか。例えば、以下の3行を含んでいるテキストファイルを対象としたとすれば 123465789123456789 1234567981234579 987654321987654321 関数を実行すれば、3という数字が得られ、 123456  789 123546879  123 123  456789123   123456789123 というテキストファイルであれば、4という数字が得られる。そんな関数をご存知な方は私にご教授願えないでしょうか。どうぞよろしくお願いします。

  • 大学でC言語やJava等を習いました.

    大学でC言語やJava等を習いました. 次のセメスターからは手続き型言語とは違う SMLという関数型言語を講義を履修しなければならないようなので, 春休み中に勉強しておこうかと思いました. しかしSMLを少しかじってみたのですが関数型言語は代入という概念もなく 数学のような感じであまりおもしろくないと感じ,関数型言語を学ぶ意欲が少し無くなってしまいました. そこで関数型言語を学ぶ意義についてどなたか教えていただけないでしょうか? よろしくおねがいします.

  • iPod shuffleのトラブルについて。

    iBookG4を使っているのですが、iPod shuffleをUSBに差し込んでも認識しなくなりました。 アップルのサポートによると、アンインストールして再度最新のiTunesとiPod UPdaterをインストールすれば解決出来るとのことだったのですが・・・。その他に記載されていたことも試しましたが、やはりダメでした。 shuffleが認識しない以外の問題はありません。 このまま認識しない場合、shuffleに保存された音楽しか聴けませんし、充電がきれてしまったら終わりということになってしまいます。購入してから一年ぐらいしかたっておりませんし、あまり使用していなかったので、なんだか嫌な感じです。 iPod shuffle自体の故障なのでしょうか? 何か解決方法があれば教えて下さい。 宜しくお願い致します。

    • ベストアンサー
    • Mac
このQ&Aのポイント
  • Win10で2台のパソコンを1つのキーボードで切り替える方法について詳しく教えてください。
  • サルでも分かるように、エレコム製品であるTK-FBM120 KBKキーボードを使用して2台のWin10パソコンを切り替える方法を教えてください。
  • エレコム株式会社の製品であるキーボードTK-FBM120 KBKを使用して、Win10で2台のパソコンを切り替える方法を教えてください。
回答を見る

専門家に質問してみよう