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?
Excel allows a max of 7 nested if statements. If we wanted to expand our list of possible statuses, we could add only one more condition and one more status.
You can have as many else if statements as necessary. In the case of many else if statements, the switch statement might be preferred for readability.
If you want to execute multiple statements for the else condition, enclose the code in curly brackets. If you only need to execute a single statement for the else condition, you do not need to use curly brackets.
A nested if in C is an if statement that is the target of another if statement. Nested if statements mean an if statement inside another if statement. Yes, both C and C++ allow us to nested if statements within if statements, i.e, we can place an if statement inside another if statement.
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)))
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 begin
s in your if
clauses. Note that if you have many side effecting computations in a conditional, you might not be using Scheme idiomatically.
@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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With