2

The question is to write a function that takes a list and partitions it into two equal-sized in one list, and returns a list whose first element is the first list and whose second element is the second list. I have a code that does that, but there are some aspects of it that I don't understand.

(here is a part of the whole code)

(define (split l)
  (define (arghelp want num call)
    (if (want num)
        num
        (num (error "No" num want call))))

Why does the if statement have no predicate? I assumed that if the want argument equaled to the num argument, then the code would run the same but it didn't.

(here is the whole code if it helps)

(define (split l)
  (define (arghelp want num call)
    (if (want num)
        num
        (num (error "No" num want call))))
  (define (take-help lst p)
    (arghelp integer? p take-help)
    (let next ((lst lst) (p p))
      (if (zero? p) '()
          (cons (car lst)
                (next (cdr lst) (- p 1))))))
  (define (drop-help lst p)
    (arghelp integer? p drop-help)
    (let next ((lst lst) (p p))
      (if (zero? p)
      lst
      (next (cdr lst) (- p 1)))))
  (let ((a (quotient (length l) 2)))
    (cons (take-help l a)
          (drop-help l a))))
Vivian
  • 31
  • 1

2 Answers2

2

Why does the if statement have no predicate?

Yes, it has a predicate:

(define (arghelp want num call)
  (if (want num)

In the above line, want is a function received as a parameter. When you called arghelp, you did it like this:

(arghelp integer? p take-help)

If you replace the argument, the condition in fact becomes:

(if (integer? num) ...

And there it is the predicate you were looking for!

Óscar López
  • 232,561
  • 37
  • 312
  • 386
2

A function is a value. eg. (lambda (v) v) gets evaluated to a function object. Often functions are bound to variables. eg.

(define identity (lambda (v) v))
(define num 19)

identity is a variable, the same as num is but the values they become when variable is evaluated are of different types. One is a function (aka closure, procedure), the other a number. All variables can be evaluated:

+ ; ==> #<procedure:+> 

#<procedure:+> is a visual representation of the core function commonly named + in racket and Racket mentions the name even though the name is irellevant. You could do this:

(define my-add +) (my-add 1 2 3) ; ==> 6

A Predicate is a function that returns a boolean #f or #t and often they have variable names ending in ? as a convention, but it is not enforced. The language doesn't have any opinion on this so it is treated like any other function by the implementation. I've seen predicated not using the convention as well ad bad code using a predicate naming scheme to something that isn't a predicate at all. The last one is actually worst of the two.

Whenever you see a variable with parentheses, like (want num) you know that either want is a function and takes num as argument or want is something else and the program will fail miserably. Thus while Scheme does not type check it will fail when you try something crazy like (num identity). You'll get an error saying that the application went terribly because num was a number and not a function.

So to your code. The programmer should perhaps made the variable name conformant to the suggestion that it is a predicate. This does not change the code the slightest, but aids reading it. Like this:

(define (arghelp predicate? value call)
  (if (predicate? value)
      value
      (value (error "Invalid value" value predicate? call))))

This still looks bad. We know that value isn't a function from it's use. Also the nature of it's use is to signal an error and what it returns isn't important. Lets fix that. I suggest it should be written like this instead:

(define (assert predicate? value call)
  (when (not (predicate? value))
    (error "No" value predicate? call)))

In the helpers you see it does:

(assert integer? p take-help)

I've renamed the function to my better name. By using substitution rules we know this would be the same as writing:

(when (not (integer? p))
  (error "No" p integer? take-help)))

Now your posted code split is strange since it checks for numbers where frmo looking at it it will never ever be anything else. What it doesn't check is if l is a list so that we can be sure length doesn't fail. Thus both calls to check for numbers can be removed and I suggest this instead:

(assert list? l split)
Sylwester
  • 47,942
  • 4
  • 47
  • 79