need help understanding letex

Started by ghyll, December 20, 2014, 11:14:37 PM

Previous topic - Next topic

ghyll

My code is a mess, and I apologize. I'm a newbie, and I got stuck trying to work out one of the bugs.



I'm trying to learn newLISP by solving some of the Project Euler problems. I'm currently working on Problem 4 using streams, using some functions from http://www.newlispfanclub.alh.net/forum/viewtopic.php?f=5&t=2162">//http://www.newlispfanclub.alh.net/forum/viewtopic.php?f=5&t=2162. (It's the task I was given by my mentor.)



I'm trying to use the 2nd syntax of letex, but I must have something wrong because I get "ERR: list or string expected in function empty? s2".


(define (product-stream-Reuler s1 s2 , s3)
  (setf s3 (if
    (empty-stream? s1) the-empty-stream
    (empty-stream? s2) the-empty-stream
    (letex (
      a1 (head s1)
      a2 (head s2)
      c1 (tail s1)
      c2 (tail s2)
      )
      (println "a1=" 'a1 ", a2=" 'a2 "ns1=" 's1 ", s2=" 's2 "nc1=" 'c1)
      (accumulate cons '()
        (cons-stream
          ; first palindrome product of (* n s2)
          (head
            (filter-stream palindrome?
              (product-stream (num-stream 'a1) 's2)
            )
          )
          ; re-run with next n
          (product-stream-Reuler 'c1 's2)
        )
      )
    )
  ))
  s3
)

; a and b are streams of the integers from 999 to 100
(product-stream-Reuler a b)


I've tried fully parenthasizing the initializers [i.e. `(a1 (head s1))`] and adding initializers for (a s1) and (c s2), but neither fixes the problem.



What bit of letex am I misunderstanding?



Thank you for your help.

ghyll

#1
I assumed it was unnecessary because I was asking a syntax question, but I'm told I should post all of my code. Apologies for length and bugs.





(set 'the-empty-stream '())

(define-macro (cons-stream)
  (letex (x (args 0) y (args 1))
    (cons x (list (delay y)))
  )
)

(define-macro (delay)
  (letex (x (args 0)) (fn () x))
)

(define (head s)
  (first s)
)

(define (tail p)
  ;; (println "printing tail: " p)
  (force (last p))
)

(define (force func)
  (func)
)

(define (empty-stream? s)
  (empty? s)
)

(define (enum-interval low high)
  (if (> low high)
    the-empty-stream
    (expand (cons-stream low (enum-interval (+ low 1) high)) 'low 'high)
    )
  )

(define (enum-interval-descending high low)
  (if (> low high)
    the-empty-stream
    (expand (cons-stream high (enum-interval-descending (- high 1) low)) 'low 'high)
  )
)

(define (num-stream n)
  (expand (cons-stream n (num-stream n)) 'n)
 )

(define (filter-stream pred s)
  (cond
    ((empty-stream? s) the-empty-stream)
    ((pred (head s))
      (letex (
        a (head s)
        b pred
        c (tail s))
      (cons-stream a (filter-stream b 'c))
      ))
    (true (filter-stream pred (tail s)))
  )
)

(define (palindrome? x)
  (= x (int (reverse (string x))))
)

; untested with stream-lists
(define (biggest? x lst)
  (> x (head lst))
)


(define (accumulate combiner init-val s)
  (if (empty-stream? s)
    init-val
    (combiner (head s) (accumulate combiner init-val (tail s)))
  )
)

(define (stream-from-list lst , s)
  (setf s
    (if (empty-stream? lst)
      the-empty-stream
      (expand (cons-stream (head lst) (stream-from-list (rest 'lst))) 'lst)
    )
  )
  s
)

(define (stream-for-each func s)
  (expand
    (if (empty-stream? s)
      the-empty-stream
      (begin
        (func (head s))
        (stream-for-each func (tail s))
      )
    )
  'func 's)
)

(define (map-stream func s)
  (if (empty-stream? s)
    the-empty-stream
    (letex (
      a (func (head s))
      b func
      c (tail s))
    (cons-stream a (map-stream b 'c))
    )
  )
)

(define (print-stream s)
  (stream-for-each println s)
)

; If (= s1 s2), only returns squares
(define (product-stream s1 s2 , s3)
 (setf s3 (if
  (empty-stream? s1) the-empty-stream
  (empty-stream? s2) the-empty-stream
  (letex (
    a1 (head s1)
    a2 (head s2)
    c1 (tail s1)
    c2 (tail s2)
    )
      (cons-stream (* a1 a2) (product-stream 'c1 'c2))
      )
  ))
 s3
)

; stream of the integer 1
(setf ones (cons-stream 1 ones))

(setf x (stream-from-list '(1 2 3)) y (stream-from-list '(1 2 3)))

; stream of integers from 999 to 100
(setf a (enum-interval-descending 999 100))
(setf b (enum-interval-descending 999 100))


;aforeposted buggy draft attempt to solve Project Euler problem 4
(define (product-stream-Reuler s1 s2 , s3)
  (setf s3 (if
    (empty-stream? s1) the-empty-stream
    (empty-stream? s2) the-empty-stream
    (letex (
      a1 (head s1)
      a2 (head s2)
      c1 (tail s1)
      c2 (tail s2)
      )
      (println "a1=" 'a1 ", a2=" 'a2 "ns1=" 's1 ", s2=" 's2 "nc1=" 'c1)
      (accumulate cons '()
        (cons-stream
          ; first palindrome product of (* n s2)
          (head
            (filter-stream palindrome?
              (product-stream (num-stream 'a1) 's2)
            )
          )
          ; re-run with next n
          (product-stream-Reuler 'c1 's2)
        )
      )
    )
  ))
  s3
)

Lutz

#2
Don't program in newLISP like you would in Scheme or other traditional LISP implementations. Your implementation relies very much on automatic reference passing of lists in Scheme and traditional LISPs. In newLISP this implementation would get slower when more elements are held in the stream queue. As lists in newLISP are passed to define functions by value (1).



The following is a FOOP implementation taking advantage of newLISP name spaces and passing of objects by reference using the FOOP - (self) function:

; FOOP stream implementation
(new Class 'Stream) ; creates a Stream context and constructor

; define Stream class methods
(context Stream)

(define (Stream:add elmnt)
    (push elmnt (self) -1))

(define (Stream:read)
    (pop (self) 1))

(context 'MAIN)

now you can do:

> (set 'st (Stream 10 20)) ; create a new stream, with optional initial elements
(Stream 10 20)
> (:add st 30)
(Stream 10 20 30)
> (:read st)
10
> st
(Stream 20 30)


> ; create a second empty stream
> (set 'a-stream (Stream)) ; create empty stream in a-stream
(Stream)
> (:add a-stream 'a)
(Stream a)
> (:add a-stream 'b)
(Stream a b)
> (:add a-stream 'c)
(Stream a b c)
> a-stream
(Stream a b c)
> (:read a-stream)
a
> (:read a-stream)
b
>  

No matter how big the stream, the FOOP implementation will always be fast. You could add more methods to the Stream class and also redefine the constructor. Doing (new Class 'Stream) is like saying:

(context 'Stream

(define (Stream:Stream) (cons (context) (args)))

(context MAIN)

For an implementation of the first 20 Euler problems see here:

http://www.newlisp.org/syntax.cgi?euler.txt">http://www.newlisp.org/syntax.cgi?euler.txt



also linked from here:

http://www.newlisp.org/index.cgi?Tips_and_Tricks">http://www.newlisp.org/index.cgi?Tips_and_Tricks



More about FOOP in the manual / reference:

http://www.newlisp.org/downloads/newlisp_manual.html#newlisp_classes">http://www.newlisp.org/downloads/newlis ... sp_classes">http://www.newlisp.org/downloads/newlisp_manual.html#newlisp_classes



and here in a newLISP introduction:

http://en.wikibooks.org/wiki/Introduction_to_newLISP/Contexts#Objects">http://en.wikibooks.org/wiki/Introducti ... ts#Objects">http://en.wikibooks.org/wiki/Introduction_to_newLISP/Contexts#Objects



(1) for other ways to pass lists by reference without using FOOP see the manual chapter:

http://www.newlisp.org/downloads/newlisp_manual.html#pass_big">http://www.newlisp.org/downloads/newlis ... l#pass_big">http://www.newlisp.org/downloads/newlisp_manual.html#pass_big

ghyll

#3
I'm new to programming, so I don't know how one programs in Scheme or other traditional LISP implementations.



Thanks for the help with streams! Using contexts with push/pop seems like a much easier way to manage the streams than trying to manipulate letex and expand for each function.



I'd still like to understand and master letex. I was told the error message is because I'm using letex incorrectly in my `product-stream-Reuler` function (included in my original question). It seems (from the error message) that still I don't quite understand it. I've read the manual and the wikibooks, and spent time playing around with the code, and no lightbulbs are going off. Am I using the wrong syntax? Am I using the correct syntax incorrectly? Or is that specific error message due to a problem somewhere else in my code?

rrq

#4
Or maybe you're using too many single-quotes in that function?

ghyll

#5
Were you referring to a specific part?



Without the single quotes, the newLISP interpreter tries to evaluate the streams using implicit indexing. This doesn't work, because (for example) there is no 998th list item in `(998 (lambda () (enum-interval-descending (- 998 1) 100)))`



http://www.newlisp.org/downloads/newlisp_manual.html#implicit_rest_slice">http://www.newlisp.org/downloads/newlis ... rest_slice">http://www.newlisp.org/downloads/newlisp_manual.html#implicit_rest_slice



I''ll run through the code again to make sure all of the quotes are necessary. Thanks for the suggestion.

rrq

#6
I can't say I've gone too deep into the code, and generally as always, it's better to follow Lutz' advice. It just looked to me like there where all too many single quotes in the function; e.g. the sub expression (product-stream (num-stream 'a1) 's2) will eventually invoke the product-stream function with the second argument being the symbol s2, which then, in that function, is assigned to be the value of s2. This (unless the first argument is an empty stream) results in empty-stream? being called with a symbol whose value is a symbol (namely s2) as argument, and thus, the primitive empty? is called with that symbol as argument. Which was the basis for my rather off-handed remark.

itistoday

#7
Quote from: "Lutz"In newLISP this implementation would get slower when more elements are held in the stream queue.


Actually, it should work just fine. The stream queue shouldn't grow much because of the lazy evaluation.
Get your Objective newLISP groove on.