Is there a possibility of multiple statements inside a conditional statement's body?

24,991

Solution 1

(let((arg1 0)(arg2 1))
  (if (> arg1 arg2) 
      (begin
        (display arg1)
        (newline)
        (display "cool"))
      (begin
        (display arg2)
        (newline)
        (display "not cool"))))

when you say (arg1 (disply "cool")) you are implying that arg1 should be a proceure.

Solution 2

One thing you may be missing is that in Scheme there is no such thing as a "statement". Everything is an expression and things you might consider statements also return a value. This applies to if, which is typically used to return a value (e.g., (if (tea-drinker?) 'tea 'coffee). Unlike C++, most uses of conditionals are not going to be for mutating a variable or printing values. This reduces the need for having multiple expressions in an if clause.

However, as Ross and Rajesh have pointed out, you can use cond (recommended) or use begins in your if clauses. Note that if you have many side effecting computations in a conditional, you might not be using Scheme idiomatically.

Solution 3

@RajeshBhat gave a good example of using begin with an if statement.

another solution is the cond form

(let ([arg1 0] [arg2 1])
  (cond
    [(< arg1 0) (display "negative!")]
    [(> arg1 arg2) (display arg1) (newline) (display "cool")]
    [else (display arg2) (newline) (display "not cool")]))

Each line in the cond form has an implicit begin which you can actually see if you look at the implementation of the cond.

(link is to the Chez Scheme documentation, might (read: probably) not be same implementation you are using as it is proprietary, though Petite Chez is free (no compiler in petite version))

http://scheme.com/tspl4/syntax.html#./syntax:s39

Edit: Important note about begin forms and therefore all expressions that have implicit begin's.

the following code

(+ 2 (begin 3 4 5))

evaluates to 7. This is because the return value of a begin form is its last expression. This is just something to keep in mind when using begins. However, using side-effects and things like displays will work just fine in the positions where the 3 and 4 are.

Solution 4

Since you are already using an iterative process in the "inner" procedure, why not use this definition using named let

(define (fact n)
  (let inner ((counter 1) (result 1))
    (if (> counter n)
        result
        (inner (+ counter 1) (* result counter)))))

Since the state of the process can be determined with just 2 variables, it will not use that much memory.

for example (fact 6) is computed like this

(inner 1 1)
(inner 2 1)
(inner 3 2)
(inner 4 6)
(inner 5 24)
(inner 6 120)
(inner 7 720)
720

Here is the letrec version of the same procedure:

(define (fact n)
  (letrec ((inner
            (lambda (counter result)
              (if (> counter n)
                  result
                  (inner (+ counter 1) (* result counter))))))
    (inner 1 1)))

Solution 5

If you feel restricted by Scheme's syntax, you can always change the syntax by defining a macro. A macro is like a lambda, except it generates code at compile-time (like a C++ template) and its arguments don't get evaluated before the macro is invoked.

You can easily make a macro to let you use the syntax that normally means procedure-application, like (arg1 "cool"), to mean "display everything inside the parentheses with a newline after each item". (It'll mean that only inside the macro, of course.) Like this:

(define-syntax print-if
  (syntax-rules ()
    [(_ c (true-print ...) (false-print ...))
      (if c
          (display-with-newlines true-print ...)
          (display-with-newlines false-print ...))]))

(define-syntax display-with-newlines
  (syntax-rules ()
    [(_ e)
      (begin (display e) (newline))]
    [(_ e0 e* ...)
      (begin (display-with-newlines e0) (display-with-newlines e* ...)) ]))

(let ([arg1 0] [arg2 1])
  (print-if (> arg1 arg2)
            (arg1 "cool")
            (arg2 "not cool")))

Output:

1
not cool

Don't worry if you don't understand how the macro definitions work right now. If you're just trying out Scheme after mastering C++, no doubt you're experiencing a lot of frustration. You should have a wee glimpse of the kind of power and flexibility Scheme really has.

A big difference between Scheme macros and C++ templates is that in a macro, the entire Scheme language is available to you. A macro tells, using Scheme, how to transform an s-expr into Scheme code, in any completely arbitrary way that you like. The compiler then compiles the Scheme code output by the macro. Since Scheme programs are themselves s-exprs, there are essentially no restrictions (other than lexical scoping and needing to enclose everything in parentheses).

And don't let anyone discourage you from using side-effects if you want to. The glory of Scheme is that you can do whatever you want.

Share:
24,991
Alex D.
Author by

Alex D.

please delete me

Updated on July 09, 2022

Comments

  • Alex D.
    Alex D. almost 2 years

    I'm primarily a C++ (thus an OO/imperative) programmer and I find it quite bizarre that you can only have one statement per evaluation in a conditional statement such as an if-statement in Scheme, a functional language.

    For example:

     (let ((arg1 0) (arg2 1))
       (if (> arg1 arg2)
           arg1
           arg2)))
    

    Erroneous example:

    (let ((arg1 0) (arg2 1))
      (if (> arg1 arg2)
          (arg1 (display "cool"))
          (arg2 (display "not cool"))))
    

    gives me an error of a type "procedure application: expected procedure, given: 2; arguments were: #void"

    That can be solved by placing that said conditional statement into different statements within a body of a defined function for example, with the conditional statement's body having separate statements every time as follows:

    (if (condition) statement1a statement2a)
    (if (condition) statement1b statement2b)
    

    and so on...

    It goes without saying that it's not too practical. Not to mention the duplicated code overhead.

    Am I missing anything here or is there really no other way?