Pattern matching with streams in Racket? - stream

Pattern matching is a very readable alternative to manual destructuring in let clauses. I was wondering whether it's possible to use it with streams, like we can with lists.
As an illustration, consider a simple add-between implementation to produce a sequence with an element inserted between each of the elements of the original sequence.
(define (add-between lst sep)
(match lst
['() '()]
[(cons v vs)
(cons v
(cons sep
(add-between vs sep)))]))
Usage:
(add-between (list 'a 'b 'c) 'and) ; => '(a and b and c and)
What if we want to do the same with arbitrary sequences, like streams?
(define (add-between seq sep)
(match seq
['() '()]
[(cons v vs)
(stream-cons v
(stream-cons sep
(add-between vs sep)))]))
This produces an error:
(add-between (stream 'a 'b 'c) 'and)
; match: no matching clause for #<stream>
; stdin:1:1
; Context:
; /Applications/Racket v7.5/collects/racket/match/runtime.rkt:24:0 match:error
; /Applications/Racket v7.5/collects/racket/repl.rkt:11:26
It would be ideal if we could pattern match on a generic sequence type, since that would encapsulate lists as well as streams.

You can define your own pattern-matching forms for streams, using define-match-expander, ? with predicates, and app with accessors.
#lang racket
(require syntax/parse/define (prefix-in * racket/stream))
(define (stream-empty? v) (and (stream? v) (*stream-empty? v)))
(define (stream-cons? v) (and (stream? v) (not (*stream-empty? v))))
(define-match-expander stream-cons
(syntax-parser
[(_ fst rst) #'(? stream-cons? (app stream-first fst) (app stream-rest rst))])
(syntax-parser
[(_ fst rst) #'(*stream-cons fst rst)]))
(define-match-expander stream*
(syntax-parser
[(_ rst) #'rst]
[(_ fst more ... rst) #'(stream-cons fst (stream* more ... rst))])
(syntax-parser
[(_ init ... rst) #'(*stream* init ... rst)]))
(define-match-expander stream
(syntax-parser
[(_) #'(? stream-empty?)]
[(_ elem ...) #'(stream* elem ... (? stream-empty?))])
(syntax-parser
[(_ elem ...) #'(*stream elem ...)]))
Using it:
> (define (add-between seq sep)
(match seq
[(stream) (stream)]
[(stream-cons v vs)
(stream-cons v
(stream-cons sep
(add-between vs sep)))]))
> (add-between (stream 'a 'b 'c) 'and)
#<stream>
> (stream->list (add-between (stream 'a 'b 'c) 'and))
'(a and b and c and)

#AlexKnauth's answer is illuminating. I've since also discovered that the data/collection library provides a built-in way to do this for arbitrary sequences (that is, sequences as defined here). Using this match pattern, the code in the question could be written for generic sequences as:
(require data/collection)
(define (add-between seq sep)
(match seq
[(sequence) empty-stream]
[(sequence v vs ...)
(stream-cons v
(stream-cons sep
(add-between vs sep)))]))
Using it:
(sequence->list (add-between (stream 'a 'b 'c) 'and))
=> '(a and b and c and)
(sequence->string (add-between "hello" #\space))
"h e l l o "

Related

Racket: How can I fix my code so that it will return all the flipped pairs that is missing?

This function should return the symmetric closure of L.
Examples:
(Symmetric-Closure'((a a) (a b) (b a) (b c) (c b))) ---> '((a a) (a b) (b a) (b c) (c b))
(Symmetric-Closure'((a a) (a b) (a c))) ---> '((a a) (a b) (a c) (b a)(c a))
(Symmetric-Closure'((a a) (b b))) ---> '((a a) (b b))
(Symmetric-Closure'())---> '()
Here is what I have in Racket
(define (Symmetric-Closure L)
;Iterate over each pair in L
(andmap (lambda (x)
;If the flipped pair does not exist in L, it will
;return L and the flipped pair that is missing. Otherwise, return L.
(if(not(member (list (cadr x)(car x)) L))
(cons (list (cadr x)(car x)) L)
(append L)))
L))
How can I fix my code so that it will return all the flipped pairs that is missing
For example, my code only return L and the last missing flipped pair (c a) instead of (b a) and (c a)
;this is wrong, it should return '((c a)(b a)(a a)(a b)(a c))
(Symmetric-Closure '((a a)(a b)(a c))-----> '((c a)(a a)(a b)(a c))
;this is correct
(Symmetric-Closure '((a a)(a b)(b a)(b c)(c b)))-----> '((a a)(a b)(b a)(b c)(c b))
andmap means "map the list using this function and then and together the results." In Racket, whenever you and together any values, the result is going to be either the last value provided to it, or false. For example, (and value1 value2) results in value2 if neither value1 nor value2 is false (and if one of them is false, the result is false as well). Since the value produced by your lambda is never false, the result of your andmap is going to be the value of the lambda expression the final time it is called, which in this case, could be the list (cons (list (cadr x)(car x)) L) for the last value of x that it sees in the original list L. This means that all preceding values that were consed don't factor into the result at all.
You could modify this to use a simple map instead. But this produces a list of lists of pairs, not a list of pairs which is what you want. So at the end you need to flatten this to arrive at the result.
(define (symmetric-closure L)
;Iterate over each pair in L
(apply append
(map (lambda (x)
;If the flipped pair does not exist in L, it will
;return L and the flipped pair that is missing. Otherwise, return L.
(if (not (member (list (cadr x) (car x)) L))
(list (list (cadr x) (car x)) x)
(list x)))
L)))
One thing to be aware of, though, is that this algorithm calls member for every element in the original list. Checking for membership in a list is O(N) and you are doing this N times, meaning that the complexity of this algorithm is O(N²). You should be able to do this more efficiently, for instance by using a hash set.

Varadic zip in Racket

I've seen the other answers on zipping functions in Racket but they are first of all not quite right (a zip should only zip up to the shortest sequence provided so that you can zip with infinite streams) and most importantly not varadic so you can only zip two streams at a time.
I have figured out this far
(define (zip a-sequence b-sequence) (for/stream ([a a-sequence]
[b b-sequence])
(list a b)))
which does work correctly
(stream->list (zip '(a b c) (in-naturals)))
=> '((a 0) (b 1) (c 2))
but is not varadic. I know I can define it to be varadic with define (zip . sequences) but I have no idea how to build the for/stream form if I do.
Does this have to be a macro to be doable?
Would this work for you?
#lang racket
(define (my-zip . xs)
(match xs
[(list x) (for/stream ([e x]) (list e))]
[(list x xs ...)
(for/stream ([e x] [e* (apply my-zip xs)])
(cons e e*))]))
(stream->list
(my-zip (in-naturals) '(a b c) '(1 2 3 4 5 6)))
;;=> '((0 a 1) (1 b 2) (2 c 3))
A common implementation of zip is:
(require data/collection) ; for a `map` function that works with streams
(define (zip . xs)
(apply map list xs))
... and if you prefer the point-free style, this can be simply:
(require racket/function)
(define zip (curry map list))
These do have the limitation that they require all input sequences to have the same length (including infinite), but since they are computed lazily, they would work in any case until one of the sequences runs out, at which point an error would be raised.
(zip (cycle '(a b c))
(naturals)
(cycle '(1 2 3))) ; => '((a 0 1) (b 1 2) (c 2 3) (a 3 1) ...
[Answer updated to reflect comments]

Associativity, Precedence and F# Pipe-forward

The F# pipe-forward can be expressed as:
let (|>) x f = f x
For example:
let SimpleFunction (a : typeA) (b : typeB) (c : typeC)=
printf "OK."
// No problem here.
SimpleFunction a b c
// Using pipe-forward
c |> SimpleFunction a b
// No problem here. Interpreted as the same as above.
However, according to the documentation, the pipe-forward operator is left-associative.
https://learn.microsoft.com/en-us/dotnet/fsharp/language-reference/symbol-and-operator-reference/
So, I expected the pipe-forward statement:
// Original Expression
c |> SimpleFunction a b
// to be equivalent to:
(c |> SimpleFunction) a b
// Which is equivalent to:
SimpleFunction c a b
// Error!! SimpleFunction takes typeA, then typeB, then typeC.
Why does the compiler not "interpret" the pipe-forward expression as the error expression? Do I have any confusion about the operator precedence/associativity?
Additional Sources:
http://theburningmonk.com/2011/09/fsharp-pipe-forward-and-pipe-backward/
What is associativity of operators and why is it important?
https://en.wikipedia.org/wiki/Operator_associativity
The associativitity of a binary operator only matters when you have two or more occurrences of the same operator. When you have different operators (here: |> and juxtaposition), what matters is their relative precedence.
Juxtaposition has a higher precedence than |>, therefore
c |> SimpleFunction a b
is parsed like
(c) |> (SimpleFunction a b)
so, by the definition of |>, it's equivalent to
(SimpleFunction a b) (c)
which would usually be written
SimpleFunction a b c
That last equivalence is due to juxtaposition being left-associative.
The fact that |> is left-associative means that an expression like
x |> f |> g
is parsed as
(x |> f) |> g
which is equivalent to
g (f x)
i.e. chains of |> express function composition — successive pipeline steps — and not passing more arguments to a function.
Functions are curried by default, you original expression is actually like this:
c |> (SimpleFunction a b) which then becomes (SimpleFunction a b) c. For this to work function application will have to have precedence.

SICP exercise 3.13 - make-cycle

I am doing exercise 3.13 from SICP but I am unsure of my answer.
Exercise 3.13: Consider the following make-cycle procedure, which uses
the last-pair procedure defined in Exercise 3.12:
(define (make-cycle x) (set-cdr! (last-pair x) x) x)
Draw a box-and-pointer diagram that shows the structure z created by
(define z (make-cycle (list 'a 'b 'c)))
What happens if we try to compute (last-pair z)?
I'm trying to understand why
(last-pair z)
becomes an infinite loop. Ignoring the box-and-pointer diagram, here is how I understand it:
(set-cdr! (last-pair x) x)
(last-pair x) would be the pair (cons 'c '()), then when we do set-cdr! the pair would become:
(cons 'c (cons 'a (cons 'b (cons 'c (cons 'a (cons 'b (cons 'c (cons 'a (cons 'b (cons 'c ...))))))))))
is my understanding correct?
No.
Your answer appears to indicate that (last-pair x) is the result of calling cons infinitely many times.
Not so.
x is still just 3 cons cells, but the last one points to the first one, creating a loop (a snake biting itself on the tail).

How to handle left recursion in FParsec

My languages uses s-expressions with one additional feature - a dot operator for accessing elements from an array or struct.
Currently, my parser works on this code using the access operator -
; Parition a sequence into a pair of sequences.
; NOTE: currently not tail-recursive.
[defRec partition [pred seq]
(if (isDone seq)
(pair (list) (list))
(let (value (peek seq))
(nextSeq (next seq))
(nextResult (partition pred nextSeq))
(nextResultFirst (access :m:first nextResult))
(nextResultSecond (access :m:second nextResult))
(if (pred value)
(pair (cons value nextResultFirst) nextResultSecond)
(pair nextResultFirst (cons value nextResultSecond)))))]
However, I want to add alternate parsing using a dot operator like so -
; Parition a sequence into a pair of sequences.
; NOTE: currently not tail-recursive.
[defRec partition [pred seq]
(if (isDone seq)
(pair (list) (list))
(let (value (peek seq))
(nextSeq (next seq))
(nextResult (partition pred nextSeq))
(nextResultFirst nextResult.first)
(nextResultSecond nextResult.second)
(if (pred value)
(pair (cons value nextResultFirst) nextResultSecond)
(pair nextResultFirst (cons value nextResultSecond)))))]
They will both parse out to an equivalent AST. Left recursion matters here because an expression like (f x).y should parse out just like (access :m:y (f x))
However, I don't know how to make FParsec deal with this type of left-recursive parsing, or what alternatives I have to left-recursion.

Resources