1
\$\begingroup\$

I know this is a bit elementary, but I am highly uncomfortable writing Scheme code, and I want to make sure that what I'm doing is good practice. I know mutation is frowned upon in Scheme.

I have no other source of review so I have come to you all.

(define (filter-lst fn lst)
    (if (not (null? lst))
    (if (fn (car lst))
        (cons (car lst) (filter-lst fn (cdr lst)))
        (filter-lst fn (cdr lst))
        )
    '()
    )
)

Note: The function seems to be working according to a few test cases I ran.

As a follow-up PURELY STYLISTIC question, which of these is preferred?

#1

(define (merge l1 l2)
      (if (null? l1) l2
          (if (null? l2) l1
              (cons (car l1) (cons (car l2) (merge (cdr l1) (cdr l2)))))))

#2

(define (merge l1 l2)
    (if (null? l1) l2
        (if (null? l2) l1
            (cons (car l1) (cons (car l2) (interleave (cdr l1) (cdr l2))))
        )
    )  
)
\$\endgroup\$
1
\$\begingroup\$

When you catch yourself using nested ifs, that's usually a sign you'd be better served by the cond form. (which is actually way more powerful than nested if due to a few quirks)

Here's how I'd clean that up

(define (filter-lst fn lst)
  (cond ((null? lst) '())
        ((fn (car lst)) 
         (cons (car lst)
               (filter-lst fn (cdr lst))))
        (else (filter-lst fn (cdr lst)))))

The above isn't entirely idiomatic (which would be to implement it as a one-liner fold with a lambda.) but does show off a few style notes. Of your choices I'd say #1 is closest to the preferred style.

First is where you may be tempted to tab, a double space will do as I do on the second line, its usually appropriate to distinct parts separate certain special forms particularly (define, let, cond). You really want to avoid creeping to the right any faster than necessarily.

(define (merge l1 l2)
  (if (null? l1) l2
      (if (null? l2) l1 (cons (car l1) (cons (car l2) (merge (cdr l1) (cdr l2)))))))

Second is that if you split a function, there should be one argument per line, aligned to the start of the first argument

(define (merge l1 l2)
  (if (null? l1) 
      l2
      (if (null? l2) 
          l1 
          (cons (car l1) 
                (cons (car l2) 
                      (merge (cdr l1) (cdr l2)))))))

And you see why cond is preffered again

(define (merge l1 l2)
  (cond ((null? l1) l2)
        ((null? l2) l1)
        (else (cons (car l1) 
                    (cons (car l2) 
                          (merge (cdr l1) (cdr l2))))))

And if a spot seems unwieldy there are often algorithmic simplifications that are worth considering

(define (merge l1 l2)
  (if (null? L1) 
      l2
      (cons (car l1)
            (merge l2 l1))))
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.