6

多くの最新のプログラミング言語では、潜在的に無限のリストを処理し、それらに対して特定の操作を実行できます。

例 [Python]:

EvenSquareNumbers = ( x * x for x in naturals() if x mod 2 == 0 )

実際に必要な要素のみが計算されるため、このようなリストが存在できます。(遅延評価)

遅延評価のメカニズムを算術演算に拡張することが可能かどうか (または、特定の言語で実践されているかどうか) を知りたいと思いました。

例: 与えられた偶数の無限リストをevens = [ x | x <- [1..], even x ] 計算できませんでした

length evens

ここで必要な計算は決して終了しないためです。

しかし、私たちは実際にそれを決定することができました

length evens > 42

length用語全体を評価する必要はありません。

これはどの言語でも可能ですか?ハスケルはどうですか?

編集:ポイントをより明確にするために:問題は、遅延リストが特定の数値よりも短いかどうかを判断する方法に関するものではありません。数値計算が遅延して行われるように、従来の組み込み関数を使用することについてです。

sdcvvc は、Haskell のソリューションを示しました。

data Nat = Zero | Succ Nat deriving (Show, Eq, Ord)

toLazy :: Integer -> Nat
toLazy 0 = Zero
toLazy n = Succ (toLazy (n-1))

instance Num Nat where
   (+) (Succ x) y = Succ (x + y)
   (+) Zero y = y
   (*) Zero y = Zero
   (*) x Zero = Zero
   (*) (Succ x) y = y + (x * y)
   fromInteger = toLazy
   abs = id
   negate = error "Natural only"
   signum Zero = Zero
   signum (Succ x) = Succ Zero

len [] = Zero
len (_:x') = Succ $ len x'

-- Test 

len [1..] < 42 

これは他の言語でも可能ですか?

4

4 に答える 4

8

独自の自然数を作成できます...

data Nat = Zero | Succ Nat deriving (Show, Eq, Ord)

len :: [a] -> Nat
len = foldr (const Succ) Zero

toLazy :: Int -> Nat
toLazy 0 = Zero
toLazy n = Succ (toLazy (n-1))

a = len [1..] > toLazy 42

その場合、遅延評価のおかげで a は True です。len が foldr を使用することが重要です。foldl は無限リストでは機能しません。また、いくつかの算術演算も実行できます (テストされていません):

instance Num Nat where
   (+) (Succ x) y = Succ (x + y)
   (+) Zero y = y
   (*) Zero y = Zero
   (*) x Zero = Zero
   (*) (Succ x) y = y + (x * y)
   fromInteger = toLazy
   abs = id
   negate = error "Natural only"
   signum Zero = Zero
   signum (Succ x) = Succ Zero

たとえば、2 * 無限大 + 3 は無限大です。

let infinity = Succ infinity

*Main> 2 * infinity + 3
(Succ (Succ (Succ ^cCc (Succ (Succ (SuccInterrupted.

2 番目の解決策

別の解決策は、() のリストを遅延自然数として使用することです。

len = map (const ())
toLazy n = replicate n () 
a = len [1..] > toLazy 42

ハックを確認してください。

編集:インスタンス番号を追加しました。

編集2:

Python への 2 番目のソリューションの翻訳:

import itertools

def length(x):
    return itertools.imap(lambda: (), x) 

def to_lazy(n):
    return itertools.chain([()] * n)

数を加算するには itertools.chain を使用し、乗算するには itertools.product を使用します。これは Haskell ほどきれいではありませんが、機能します。

クロージャを持つ他の厳格な言語では、型 () -> a の代わりに a を使用して遅延をシミュレートできます。これを使用して、最初のソリューションを Haskell から他の言語に翻訳することができます。ただし、これはすぐに読めなくなります。

Python one-liners に関する素敵な記事も参照してください。

于 2009-09-20T16:32:43.540 に答える
3

怠惰がなければ、これはF#で機能すると思います。

type Nat = Zero | Succ of Nat

let rec toLazy x =
    if x = 0 then Zero
    else Succ(toLazy(x-1))

type Nat with
    static member (+)(x:Nat, y:Nat) =
        match x with
        | Succ(a) -> Succ(a+y)
        | Zero -> y
    static member (*)(x:Nat, y:Nat) =
        match x,y with
        | _, Zero -> Zero
        | Zero, _ -> Zero
        | Succ(a), b -> b + a*b

module NumericLiteralQ =
    let FromZero()          =  toLazy 0
    let FromOne()           =  toLazy 1
    let FromInt32(x:int)    =  toLazy x

let rec len = function
    | [] -> 0Q
    | _::t -> 1Q + len t

printfn "%A" (len [1..42] < 100Q)
printfn "%A" (len [while true do yield 1] < 100Q)

しかし、私たちは怠惰である必要があるので、それはこれに拡大します:

let eqLazy<'T> (x:Lazy<'T>) (y:Lazy<'T>) : bool =  //'
    let a = x.Force()
    let b = y.Force()
    a = b

type Nat = Zero | Succ of LazyNat
and [<StructuralComparison(false); StructuralEqualityAttribute(false)>]
    LazyNat = LN of Lazy<Nat> with
        override this.GetHashCode() = 0
        override this.Equals(o) =
            match o with
            | :? LazyNat as other -> 
                let (LN(a)) = this
                let (LN(b)) = other
                eqLazy a b
            | _ -> false
        interface System.IComparable with
            member this.CompareTo(o) =
                match o with
                | :? LazyNat as other ->
                    let (LN(a)) = this
                    let (LN(b)) = other
                    match a.Force(),b.Force() with
                    | Zero, Zero   -> 0
                    | Succ _, Zero -> 1
                    | Zero, Succ _ -> -1
                    | Succ(a), Succ(b) -> compare a b
                | _ -> failwith "bad"

let (|Force|) (ln : LazyNat) =
    match ln with LN(x) -> x.Force()

let rec toLazyNat x =
    if x = 0 then LN(lazy Zero)
    else LN(lazy Succ(toLazyNat(x-1)))

type LazyNat with
    static member (+)(((Force x) as lx), ((Force y) as ly)) =
        match x with
        | Succ(a) -> LN(lazy Succ(a+ly))
        | Zero -> lx

module NumericLiteralQ =
    let FromZero()          =  toLazyNat 0
    let FromOne()           =  toLazyNat 1
    let FromInt32(x:int)    =  toLazyNat x

let rec len = function
    | LazyList.Nil -> 0Q
    | LazyList.Cons(_,t) -> LN(lazy Succ(len t))

let shortList = LazyList.of_list [1..42]
let infiniteList = LazyList.of_seq (seq {while true do yield 1})
printfn "%A" (len shortList < 100Q)      // true
printfn "%A" (len infiniteList < 100Q)   // false

これは、人々がHaskellでこのようなものだけを書く理由を示しています。

于 2009-09-20T18:47:14.357 に答える
2

偶数から42を超える要素を取得しようとすると、おそらく目的を達成できます。

于 2009-09-20T16:01:58.247 に答える
1

あなたの質問がわかりませんが、これを試してみましょう。おそらくあなたはストリームを探しています。私はFP言語族の中でErlangしか話せないので...

esn_stream() ->
  fun() -> esn_stream(1) end.

esn_stream(N) ->
  {N*N, fun() -> esn_stream(N+2) end}.

ストリームを呼び出すと、常に次の要素が返されます。次の要素を取得するために呼び出す必要があります。このようにして、遅延評価を実現します。

次に、is_stream_longer_than関数を次のように定義できます。

is_stream_longer_than(end_of_stream, 0) ->
  false;
is_stream_longer_than(_, 0) ->
  true;
is_stream_longer_than(Stream, N) ->
  {_, NextStream} = Stream(),
  is_stream_longer_than(NextStream, N-1).

結果:

1> e:is_stream_longer_than(e:esn_stream(), 42).
true

2> S0 = e:esn_stream().
#Fun<e.0.6417874>

3> {E1, S1} = S0().
{1,#Fun<e.1.62636971>}
4> {E2, S2} = S1().
{9,#Fun<e.1.62636971>}
5> {E3, S3} = S2().
{25,#Fun<e.1.62636971>}
于 2009-09-20T16:08:34.787 に答える