2016-10-04 11 views
0

私は、リスト内の1つの数を見つけることfoldrlambdaを使用する方法を見つけたため、リストを検索するためにラケットを使用しました。リストは一つだけ1一つだけ「1」

(define (exactlyone L) 
    (foldr (lambda (elem count) (if (equal? elem 1) (+ count 1) count)) 
     0 L) 

) 

を持っている場合しかし、どのように可能な場合であれば条件でcount値を使用することができますどのように確認するためにif条件や他の方法を使用するには?

答えて

1

すべてのリストをトラバースするまで、1の数がわからないので、必ずfoldrはすべてのアイテムを消費する必要があります。 countの戻り値が1した場合はその後、それはテストの単純な問題だ。もちろん

(define (exactlyOne L) 
    (= 1 
    (foldr (lambda (elem count) 
       (if (equal? elem 1) 
        (+ count 1) 
        count)) 
      0 
      L))) 

、最も簡単な方法は、代わりに車輪の再発明の、(のようなcount)既存の手順を使用することです。これは、ラケットに動作します:たとえば

(define (exactlyOne lst) 
    (= 1 
    (count (curry equal? 1) lst))) 

(exactlyOne '(1 2 3)) 
=> #t 

(exactlyOne '(1 2 3 1)) 
=> #f 
0

最も簡単な方法は、あなた自身の再帰行うには、次のようになります。あなたは折り目でそれを解決したい場合は

(define (only-one predicate lst) 
    (let loop ((lst lst) (seen #f)) 
    (cond ((null? lst) seen) 
      ((not (predicate (car lst))) (loop (cdr lst) seen)) 
      ;; from here on we know predicate is true 
      (seen #f) ; at least two 
      (else (loop (cdr lst) #t))))) ; recurse with seen as #t 

をあなたはできます:

(define (only-one predicate lst) 
    (call/cc 
    (lambda (return) 
    (foldl (lambda (e seen) 
       (cond ((not (predicate e)) seen) ; keep seen (whatever it is) 
        (seen (return #f))   ; short circuit at second match 
        (else #t)))    ; change seen to #t 
      #f 
      lst)))) 

すべての要素が処理される前に結果を知っている場合は、call/ccを使用して終了プロシージャを取得します。 1つ以上一致しない場合は#f、1つしかない場合は#tになります。

このような両作品:

(only-one (lambda (x) (equal? x 1)) '(0 1 2 3 4 5)) ; ==> #t 
(only-one (lambda (x) (equal? x 1)) '(2 3 4 5))  ; ==> #f 
(only-one (lambda (x) (equal? x 1)) '(0 1 2 1 3 4 5)) ; ==> #f 
0

あなたが変更可能な変数を含むレキシカルスコープをキャプチャするために倍のカーネル関数を許可する場合、この問題が解決することができます。次に、アキュムレータタイプのBooleanを保持することができますが、計算を実行するのに十分な状態があります。

擬似コード:

(foldl (let ([s 0]) 
     (lambda (accum item) 
      ;; if (equal? item 1) and s is not already 2 
      ;; increment s 
      ;; return (equal? s 1) 
     ) 
     #f list) 

あなたはどのような環境をキャプチャしていない機能でこれを解決することはできません。なぜそれに制限するのですか?関数は、コードと定義上の字句環境です。上記で

、アキュムレータは、基本的にはダミーです。 sという州は私たちが必要とするすべてを表しているので、私たちはそれを見ていません。ブールsを使用して、状態がアキュムレータパラメータとsの情報の組み合わせであるようにすることができます。ブールアキュムレータとブール値sの間で状態を分割することができます(これらが一緒になって必要な3つの状態を表す2ビットカウンタを形成します)。ここで

は、それが変更可能な環境なしでちょうどブールを返す関数では解決できないという非公式の証拠である:

  1. は、結果がブールなければならないことを観察:正確に一つ1があり、正しいか間違っているか?したがって、フォールトカーネルとして使用する関数は、アキュムレータが返されるものなので、ブールアキュムレータを持つ必要があります。

  2. フォールドのアキュムレータは、カーネル関数のアルゴリズムが決定を下す状態全体をカプセル化します。たとえば、関数が可変変数を含む字句スコープを使用すると、不正行為になります。

  3. アルゴリズムでは、アキュムレータに少なくとも3つの状態が必要です。アキュムレータは、それが別の1を見たとき、それはS2に移行し、そこから1が分かるS1、に遷移そこからいくつかの初期S0、でなければなりません。このアキュムレータは、S0と偽を示すS2、そしてS1が真となるように、折り返しの外側で解釈する必要があります。

  4. 理論的には訪問先アイテム間でアキュムレータタイプを変更することはできますが、これに関する情報はありません。どの要素が最後であるかは知らされていません。最後の要素を見ていることが分かっていれば、トライステートアキュムレータをブール値に戻してそれを返すことができます。

Sylwesterの答えの2番目の部分は継続を使用する理由はここにある:その後、代わりにS2への移行のアルゴリズムは、直接倍の外に脱出し、虚偽を生成することができます。アキュムレータはブール値になります。 (字句ブロックからの復帰などの完全な吹き飛ばされた継続の代わりに、はるかに単純な非局所的出口機構で十分である)。

関連する問題