7

グーグルで調べてみると、whileループを使用したり、変数を使用したりすることはお勧めできません。

ここで、入力ストリームから文字を読み取り、それに応じて解析する非常に単純なアルゴリズムを実装しました。入力の場合10:abcdefghejは解析し10、コロンの次の10バイトを読み取ります。

私が少し迷っているのは、変数に依存しないようにこれをリファクタリングする方法です。


(defn decode-string [input-stream indicator]

  (with-local-vars [length (str (char indicator) )
            delimiter (.read input-stream ) 
            string (str "")
            counter 0 ]

    (while (not(= (var-get delimiter) 58 ))
       (var-set length (str (var-get length) (char (var-get delimiter)) ))
       (var-set delimiter (.read input-stream )))

    (var-set length (new BigInteger (var-get length)) )
    (var-set counter (var-get length))

    (while (not(zero? (var-get counter) ))
       (var-set string (str (var-get string) (char (.read input-stream ))  ))
       (var-set counter (dec (var-get counter))))
    (var-get string)))

また、変数を宣言する唯一の方法はwith-local-varsキーワードを使用することであることを理解しています。最初にすべての変数を1つのブロックで定義するのは非現実的ではありませんか、それともいくつかの重要なポイントが欠けていますか?

4

4 に答える 4

18

あなたが書いているのは、lispのような構文を持つCコードです(攻撃は意図されていません)。自分がしていないことでスタイルを定義することは非常に明確ですが、「それでは、他にどのように」を知らなければ、あまり役に立ちません。

ちなみに、どうしたらいいのかわかりませindicatorん。

これが私がこの問題に取り組む方法です:

  1. 問題には2つの部分があります。読み取る文字数を見つけてから、その数の文字を読み取ります。したがって、2つの関数を記述read-countread-itemます。後者は前者を使用します。

    (defn read-count [stream]
      ;; やること
      )。
    
    (defn read-item [stream]
      ;; やること
      )。
    
  2. read-itemまず、読み取る文字数を決定する必要があります。そのために、read-countこれからも定義する便利な関数を使用します。

    (defn read-item [stream]
      ([カウント(読み取りカウントストリーム)]
        ;; やること
        ))
    
  3. ループはClojureで行われ、通常はとを使用して処理するのが最適loopですrecurloopのような変数もバインドしますletacc読み取られたアイテムを蓄積することを目的としていますが、その場で変更されるのではなく、反復ごとに再バインドされることに注意してください。

    (defn read-item [stream]
      (ループ[カウント(読み取りカウントストリーム)
             acc ""]
        ;; やること
        (recur(dec count);countの新しい値
               (str acc c))))); accの新しい値
    
  4. 次に、そのループで何かを行う必要があります。c次の文字にバインドしますが、が0 のacc場合に戻ります。は。と同じです。なじみのない人のために、フォームに少し注釈を付けました。count(zero? count)(= count 0)if

    (defn read-item [stream]
      (ループ[カウント(読み取りカウントストリーム)
             acc ""]
        (if(ゼロ?カウント);条件
            acc; それから
            (let [c(.read stream)]; \
              (recur(dec count);> else
                     (str acc c)))))))); /
    
  5. 今必要なのはread-count関数だけです。同様のループを使用します。

    (defn read-count [stream]
      (ループ[カウント0]
        ([c(.read stream)]
          (if(= c ":")
              カウント
              (繰り返し(+(*カウント10)
                        (Integer / parseInt c))))))))
    
  6. REPL、デバッグ、リファクタリングでテストします。.read本当に文字を返しますか?整数を解析するためのより良い方法はありますか?

私はこれをテストしておらず、Clojure(私はCommon Lispを主に使用しています)の経験も深い知識もないことで少し妨げられていますが、この種の問題に「リズピー」な方法で取り組む方法を示していると思います。変数の宣言や変更について私が考えていないことに注意してください。

于 2009-06-28T12:25:44.903 に答える
10

このパーティーには少し遅れていると思いますが、文字列を一連の文字として扱い、Clojure のシーケンス処理プリミティブを使用すると、問題ははるかに簡単になります。

(defn read-prefixed-string [stream]
  (let [s (repeatedly #(char (.read stream)))
        [before [colon & after]] (split-with (complement #{\:}) s)
        num-chars (read-string (apply str before))]
    (apply str (take num-chars after))))

user> (let [in (java.io.StringReader. "10:abcdefghij5:klmnopqrstuvwxyz")]
        (repeatedly 2 #(read-prefixed-string in)))
("abcdefghij" "klmno")

まとめ:

  • 醜い副作用のある入力ストリームを文字の遅延シーケンスに変換して、この操作の残りの部分で単なる文字列であるかのように装うことができるようにします。ご覧のとおり、結果を計算するのに必要な文字数よりも多くの文字が実際にストリームから読み取られることはありません。
  • 文字列を 2 つの部分に分割します。最初のコロンの前の前半の文字と、残りの後半の文字です。
  • before分解を使用して、これらの部分をおよびという名前のローカルにバインドし、記述しやすいように名前を付けた未使用のローカルにバインドすることで、afterその部分を取り除きます。:colon
  • beforeその数値を取得するために読み取ります
  • からその多くの文字afterを取得し、それらをすべて一緒に文字列にマッシュします(apply str)

Svante の回答は、Clojure でループっぽいコードを記述する方法の優れた例です。私の例が、必要なことを実行できるように組み込み関数を組み立てる良い例であることを願っています。確かに、これらはどちらも C ソリューションを「非常に単純」とは言えません!

于 2011-09-04T09:50:11.613 に答える
6

Idomatic Clojure は、シーケンスの操作に非常に適しています。C では、変数の観点から考えたり、変数の状態を何度も変更したりする傾向があります。Clojure では、シーケンスの観点から考えます。この場合、問題を 3 つの抽象化レイヤーに分割します。

  • ストリームを一連のバイトに変換します。
  • 一連のバイトを一連の文字に変換する
  • 文字列を文字列列に変換します。

バイトへのストリーム:

defn byte-seq [rdr]  
  "create a lazy seq of bytes in a file and close the file at the end"  
  (let [result (. rdr read)]  
    (if (= result -1)  
      (do (. rdr close) nil)  
      (lazy-seq (cons result (byte-seq rdr))))))  

バイトから文字へ

(defn bytes-to-chars [bytes]
  (map char bytes))

文字から文字列へ [chars]

(defn chars-to-strings [chars]
   (let [length-str (take-wile (#{1234567890} %) chars)
         length (Integer/parseInt length-str)
         length-of-lengh (inc (count length-str)) 
         str-seq (drop length-of-length chars)]
        (lazy-seq 
          (cons
            (take length str-seq)
            (recur (drop (+ length-of-length length) chars))))))

これは遅延評価されるため、次の文字列が必要になるたびに、入力ストリームから引き出されて構築されます。たとえば、最初にストリーム全体をバッファリングしたり、このストリームから読み取るコードがどのように構築されるかを心配したりすることなく、ネットワーク ストリームでこれを使用できます。

ps: 現時点では私の REPL ではないので、編集してエラーを修正してください :)

于 2010-02-01T23:33:53.030 に答える
3

私は Clojure を自分で学んでいるので、これは教祖のアドバイスではなく、仲間の学生のアドバイスとして受け取ってください。

Clojure は関数型プログラミング言語です。関数型プログラミングとは、ループ、変数、および副作用がないことを意味します。これらの 3 つのルールから逸脱した場合は、そうする非常に正当な理由が必要であり、正当な理由はほとんどありません。

あなたは明らかに非常に熟練したプログラマーなので、この情報を見て、機能設計がオブジェクト指向設計とどのように異なるかについて、より多くのアイデアを得ることができれば幸いです.

http://en.wikibooks.org/wiki/Clojure_Programming/Concepts#Sequence_Functions

また、いくつかの clojure コードを確認することをお勧めします。これは github.com でホストされているサンプル プログラムで、clojure スクリーンキャスト チュートリアルの一部として作成されました。

http://github.com/technomancy/mire/tree/master

コードが意図された screencast-tutorial はここにありますが、無料ではありません:

http://peepcode.com/products/functional-programming-with-clojure

(とにかく、私は peepcode.com と提携していません)。

Clojureで頑張ってください!

于 2009-06-28T11:14:56.450 に答える