Pergunta

Muitas linguagens de programação modernas nos permitem lidar com listas potencialmente infinitas e para executar certas operações sobre eles.

Exemplo [Python]:

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

Essas listas podem existir porque apenas os elementos que são realmente necessários são computados. (Avaliação preguiçoso)

Eu só queria saber por interesse se é possível (ou mesmo praticado em certas línguas) para estender o mecanismo de avaliação preguiça de aritmética.

Exemplo: Dada a lista infinita de números pares evens = [ x | x <- [1..], even x ] Nós não poderia computar

length evens

desde o computação necessária aqui nunca iria terminar.

Mas nós realmente pode determinar que

length evens > 42

sem ter que avaliar todo o período length.

Isso é possível em qualquer idioma? E sobre Haskell?

Edit: Para fazer o ponto mais claro: A questão não é sobre como determinar se uma lista preguiçoso é menor do que um determinado número. É sobre como utilizar funções internas convencionais de uma forma que a computação numérica é feito preguiçosamente.

sdcvvc mostrou uma solução para 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 

É este também é possível em outros idiomas?

Foi útil?

Solução

Você pode criar seus próprios números naturais ...

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

Em seguida, uma é verdadeira, graças a avaliação preguiçosa. É crucial que os usos len foldr, foldl não funciona em listas infinitas. E você pode fazer alguma aritmética também (não testado):

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

Por exemplo, 2 * + 3 infinito é infinito:

let infinity = Succ infinity

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

Segunda solução

Outra solução é usar listas de () como números naturais preguiçosos.

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

Verifique Hackage .

Edit:. Instância acrescentou Num

Edit2:

Tradução de segunda solução para Python:

import itertools

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

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

Para adicionar números, utilize itertools.chain, para multiplicar, use itertools.product. Isto não é tão bonita quanto em Haskell, mas funciona.

Em qualquer outra língua estrita com fechos, você pode simular a preguiça usando o tipo () -> a vez de um. Usando que é possível traduzir a primeira solução de Haskell para outras línguas. Isto irá obter rapidamente ilegível, no entanto.

Veja também um artigo agradável em Python um- forros .

Outras dicas

Se não fosse para a preguiça, acho que isso iria trabalhar em 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)

Mas uma vez que precisamos de ser preguiçoso, ele se expande para o seguinte:

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

Isso demonstra por que as pessoas só escrever este material em Haskell.

Você provavelmente pode conseguir o que deseja, tentando obter mais de 42 elementos de Evens.

Não sei se entendi sua pergunta, mas vamos tentar dar um presente. Talvez você está procurando córregos. Eu só falo Erlang, fora da família das línguas FP, então ...

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

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

Chamando o fluxo sempre retorna o próximo elemento, e uma diversão que você deve chamar para recuperar o próximo elemento. Desta forma, você alcançar a avaliação preguiçosa.

Em seguida, você pode definir uma função is_stream_longer_than como:

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).

Resultado:

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>}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top