I'm playing around with HTML parsing as a learning exercise. The current exercise is writing a look-down function which can be handed an HTML value produced by the html library and a function, run the function across the collect the results, and return them. I would use this for things like "find every link in the following document" or "capitalize every use of the word 'kumquat'" or etc.
I would like to have a way to say "this element isn't interesting, just ignore it" so that I don't get spurious return values in the results list. I can't see a way to do that...I can exclude the values afterwards, but that's not really what I was looking for, and opens the door to false negatives.
Here's some simplified test code (removing all the HTML stuff for clarity) to show the point:
----------------
(define (foo-1 x) (if (eq? x "foo") #t '()))
(define (foo-2 x) (if (eq? x "foo") #t (values)))
(map foo-1 '(a b "foo" "bar"))
(filter (lambda (x) (not (null? x))) (map foo-1 '(a b "foo" "bar")))
(map foo-2 '(a b "foo" "bar"))
[dstorrs@localhost:~/personal/study/scheme/spider:<master*>]$ racket test.rkt
racket test.rkt
'(() () #t ())
'(#t)
result arity mismatch;
expected number of values not received
expected: 1
received: 0
values...:
context...:
/Users/dstorrs/personal/study/scheme/spider/test.rkt: [running body]
----------------
Instead of a 'result arity' crash, I would have liked to get '(#t) back, same a if I'd generated the list and then filtered it.
Is there a way to do this?
For the record, here's the actual look-down function:
(require (prefix-in h: html)
(prefix-in x: xml))
(define (look-down el action)
(match el
[(struct h:html-full (attributes content))
(apply append (map action content))]
[(struct h:html-element (attributes))
(action el)]
[ _ '()]))
#lang racket
(require rackunit)
(define (foo-3 x) (if (eq? x "foo") (list #t) empty))
(check-equal? (append-map foo-3 '(a b "foo" "bar")) '(#t))
Hi Jay,
I actually tried that in my foo-2 function: (define (foo-2 x) (if (eq? x "foo") #t (values)))
That's the one that led to the arrity contract failure.
(And I just realized that I should have named them foo1 and foo2 because it looks like I named it "foo minus 2". Oh well.)
---------------
Matthew Butterick wrote:
> Answer: Yes, if you redefine nothingness. You can exploit the fact that
> appending (or splicing) an empty list will cause it to disappear (meaning, no
> #<void> residue).
> #lang racket
> (require rackunit)
> (define (foo-3 x) (if (eq? x "foo") (list #t) empty))
> (check-equal? (append-map foo-3 '(a b "foo" "bar")) '(#t))
That was exactly what I wanted! Thank you very much. Also, I hadn't yet discovered "append-map", so that's another thank you.
PS. I'm assuming that you're using `eq?` here in deliberate preference to `equal?`. Because `eq?` is not reliable for string comparisons.
On Dec 11, 2015, at 5:27 PM, David Storrs <david....@gmail.com> wrote:On Fri, Dec 11, 2015 at 1:53 PM, Matthew Butterick <chro...@gmail.com> wrote:PS. I'm assuming that you're using `eq?` here in deliberate preference to `equal?`. Because `eq?` is not reliable for string comparisons.Ah. No, I did not realize that. I thought that Racket worked on a flyweight pattern where all strings of the same characters were eq? -- isn't that what interned symbols are about?
-Jon