Существует ли возможность множественных утверждений внутри тела условного оператора?

Я в первую очередь программист С++ (таким образом, OO/императив), и я нахожу довольно причудливым, что вы можете иметь только один оператор для оценки в условном выражении, например if-statement в Scheme, функциональном языке.

Например:

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

Неверный пример:

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

дает мне ошибку типа "процедура приложения: ожидаемая процедура, данная: 2; аргументы: #void"

Это можно решить, помещая указанный условный оператор в разные операторы внутри тела определенной функции, например, с телом условного оператора, имеющим отдельные инструкции каждый раз следующим образом:

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

и т.д.

Само собой разумеется, что это не слишком практично. Не говоря уже о дублировании служебных данных кода.

Мне что-то не хватает или нет другого пути?

Ответ 1

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

когда вы говорите (arg1 (disply "cool" )), вы подразумеваете, что arg1 должен быть процедурой.

Ответ 2

Одна вещь, которую вам может не хватать, заключается в том, что в Схеме нет такой вещи, как "выражение". Все это выражение, и вы можете считать, что утверждения также возвращают значение. Это относится к if, который обычно используется для возврата значения (например, (if (tea-drinker?) 'tea 'coffee). В отличие от С++, большинство применений условных выражений не предназначены для изменения значений переменных или печати, что уменьшает необходимость наличия нескольких выражений в предложении if.

Однако, как указал Росс и Раджеш, вы можете использовать cond (рекомендуется) или использовать begin в своих предложениях if. Обратите внимание, что если у вас много побочных эффектов в условном выражении, вы можете не использовать Схему идиоматически.

Ответ 3

@RajeshBhat дал хороший пример использования begin с инструкцией if.

другое решение - это форма cond

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

Каждая строка в форме cond имеет неявный begin, который вы действительно можете увидеть, если вы посмотрите на реализацию cond.

(ссылка на документацию Chez Scheme, возможно (читайте: возможно) не будет той же самой реализацией, которую вы используете, поскольку она является собственностью, хотя Petite Chez свободен (нет компилятора в миниатюрной версии))

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

Изменить: Важное примечание о начинающих формах и, следовательно, во всех выражениях, которые имеют неявное начало.

следующий код

(+ 2 (begin 3 4 5))

имеет значение 7. Это связано с тем, что возвращаемое значение формы begin является его последним выражением. Это просто что-то, о чем следует помнить при начале использования. Однако использование побочных эффектов и таких вещей, как дисплеи, будет очень хорошо работать в позициях, где находятся 3 и 4.

Ответ 4

Поскольку вы уже используете итеративный процесс во внутренней процедуре, почему бы не использовать это определение, используя named let

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

Поскольку состояние процесса может быть определено только с двумя переменными, оно не будет использовать столько памяти.

например (факт 6), вычисляется следующим образом:

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

Вот версия letrec той же процедуры:

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

Ответ 5

Если вы чувствуете ограниченность синтаксисом Scheme, вы всегда можете изменить синтаксис, указав макрос. Макрос похож на лямбда, за исключением того, что он генерирует код во время компиляции (например, шаблон С++), и его аргументы не обрабатываются до вызова макроса.

Вы можете легко сделать макрос, чтобы вы могли использовать синтаксис, который обычно означает процедуру-приложение, например (arg1 "cool"), чтобы означать "отображать все в круглых скобках с новой строкой после каждого элемента". (Это будет означать, что только внутри макроса, конечно.) Например:

(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")))

Вывод:

1
not cool

Не волнуйтесь, если вы не понимаете, как работают макроопределения. Если вы просто пробуете Scheme после освоения С++, вы, несомненно, испытываете много разочарований. У вас должно быть небольшое представление о силе и гибкости. Схема действительно есть.

Большая разница между макросами Scheme и шаблонами С++ заключается в том, что в макросе доступен весь язык Схемы. Макрос сообщает, используя Scheme, как преобразовать s-expr в код схемы, любым абсолютно любым способом, который вам нравится. Затем компилятор компилирует код схемы, выводимый макросом. Поскольку программы Scheme сами являются s-exprs, по существу нет ограничений (кроме лексического охвата и необходимости заключать все в круглые скобки).

И не позволяйте никому препятствовать вам использовать побочные эффекты, если хотите. Слава Схемы заключается в том, что вы можете делать все, что хотите.