Вопрос

Я слежу за Структура и интерпретация компьютерных программ и при попытке решить пример 1.3 я пришел к следующему коду в качестве моей первой попытки:

(define (sumsq a b c)(
(define highest (if (> (if (> a b) a b) (if (> a c) a c)) (if (> a b) a b) (if (> a c) a c)))
(define second_h (if (> (if (> a b) b a) (if (> a c) c a)) (if (> a b) b a) (if (> a c) c a)))
(+ (* highest highest) (* second_h second_h)))

Это не работало, и я поискал решение и нашел их по адресу SICP Вики

;; ex 1.3
;; implemented using only techniques covered to this point

(define (square x) (* x x))

(define (sum-of-squares x y)
  (+ (square x) (square y)))

(define (largest-two-of-three x y z)
  (if (>= x y)
      (sum-of-squares x (if (>= y z) y z))
      (sum-of-squares y (if (>= x z) x z))))

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

Являются ли функции в схеме однострочными?Или я пропустил все это?

Это было полезно?

Решение

То, что вы написали (за вычетом одного дополнительного родителя), это:

(define (sumsq a b c)
  (define highest
    (if (> (if (> a b) a b)
           (if (> a c) a c))
      (if (> a b) a b)
      (if (> a c) a c)))
  (define second_h
    (if (> (if (> a b) b a)
           (if (> a c) c a))
      (if (> a b) b a)
      (if (> a c) c a)))
  (+ (* highest highest) (* second_h second_h)))

Их решение разделяет квадраты и сумму квадратов на отдельные функции, но я не думаю, что это важно.Не пишу (+ (* a a) (* b b)) будет избавит вас от необходимости называть два вычисляемых значения, что позволит вам записать функцию в виде одного большого выражения в конце, но сейчас есть о чем побеспокоиться.

Я думаю, проблема, с которой вы столкнулись, заключается в том, что ваши выражения (если ...) слишком велики, чтобы их было легко понять.Обратите внимание, что есть два паттерна, которые появляются много раз: (if (> a b) a b) и (if (> a b) b a).Это функции max и min, поэтому полезно определить их как таковые:

(define (min a b) (if (< a b) a b))
(define (max a b) (if (< a b) b a))

Таким образом, вы можете переписать свое решение следующим образом:

(define (sumsq a b c)
  (define highest
    (if (> (max a b) (max a c))
      (max a b)
      (max a c)))
  (define second_h
    (if (> (min a b) (min a c))
      (min a b)
      (min a c)))
  (+ (* highest highest) (* second_h second_h)))

Упрощение этого снова дает:

(define (sumsq a b c)
  (define highest
    (max (max a b) (max a c)))
  (define second_h
    (max (min a b) (min a c)))
  (+ (* highest highest) (* second_h second_h)))

Обратите внимание, что с этим текстом гораздо легче рассуждать, (max (max a b) (max a c)) очевидно, что это максимум из a b и c, и на самом деле может быть переписан как (max (max a b) c).Глядя на second_h, хотя не очевидно, что это правильно.Что произойдет , когда a является наименьшим из трех значений?

Хитрость, которую они используют в своем решении, заключается в том, чтобы сначала сравнить x и y.если x < y, тогда вы знаете , что y не является самым маленьким из трех, поэтому он либо самый высокий, либо второй по величине.Другое число, которое вы захотите использовать, является более высоким из x и z, поскольку меньший из этих двух будет наименьшим из трех, которые вы хотите проигнорировать.Аналогичная логика применима , когда y < x.

Другие советы

Вы должны использовать правильные отступы и разрывы строк, чтобы получить общее представление о потоке вашей программы.Тогда ваше первое предложение звучит примерно так:

(define (sumsq a b c)
  ((define highest 
     (if (> (if (> a b) a b)
            (if (> a c) a c))
         (if (> a b) a b)
         (if (> a c) a c)))
   (define second-h
     (if (> (if (> a b) b a)
            (if (> a c) c a))
         (if (> a b) b a)
         (if (> a c) c a)))
   (+ (* highest highest)
      (* second-h second-h)))

Первое, на что следует обратить внимание:скобки не совпадают;есть еще один открытый, чем закрытый.Тщательный осмотр показывает, что одна открывающая скобка во второй строке неправильная.Это, кстати, тот самый, который каким-то образом болтался в конце вашей первой строки.Я бы рискнул предположить, что, когда вы попытались оценить это, ничего не произошло, поскольку читатель ждал окончания высказывания.

Правильный отступ очень важен.Я думаю, что SICP явно не объясняет это, хотя примеры обычно делаются таким образом.Я нашел руководство по стилю здесь.

Второе наблюдение:Ты много повторяешься.Во всех этих вложенных if заявления, я не совсем уверен, действительно ли вы вывели правильные значения.Посмотрите на найденное вами решение, чтобы увидеть, как это можно значительно упростить.

Вы попытались устранить сложность, дав имена вложенным результатам.Разделение сложности - это хорошо, но, как правило, лучше называть не результаты, а концепции.Подумайте о том, что вы делаете, затем назовите эти виды деятельности.Это функции, и они составляют язык, на котором вы, наконец, почти тривиально решаете свою проблему.

Одна из идей Схемы заключается в bottom-up programming где вы создаете функцию для каждой концептуальной операции.Это рекомендуемый подход во многих функциональных языках программирования.

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

Ваше решение имело такую форму:(определить (параметр функции) (определить...) (определить...))

Но define нуждается в этой форме:(определить (функциональный параметр) body)

Тело - это реализация функции...что он делает, что он возвращает.Твое тело было просто более определенным, никогда ничего не делающим.Таким образом, это объясняет, почему ваше решение не было принято интерпретатором Scheme.

Чтобы ответить на вопрос "являются ли функции scheme однострочными?" вам нужно исследовать форму "begin", которая выглядит следующим образом:(начать (+ 1 1) (+ 2 2)) => 4

В приведенном выше примере результат (+ 1 1) просто выбрасывается way , так что вы можете видеть, что begin действительно имеет смысл только тогда, когда содержимое внутри него имеет побочные эффекты.

Вы должны знать, что некоторые части Scheme (в частности, let и lambda) имеют неявные начала вокруг своего тела.Так что это действительно:

  (let ((x 1))
    (+ 1 1)
    (+ 2 2))

даже без начала.Это упрощает написание кода.

Наконец, продолжая изучать Схему, всегда старайтесь найти способ сделать что-то без начала и побочных эффектов.Особенно в первых нескольких главах большинства книг о схемах, если вы думаете: "Я хочу установить эту переменную, затем я хочу сделать это, затем это ...", вы, вероятно, попали в ловушку своего старого способа программирования и не делаете это по схеме.В побочных эффектах вообще нет ничего плохого, но их интенсивное использование означает, что вы на самом деле программируете Схему не так, как она работает лучше всего.

В упражнении 1.3 вам предлагается определить процедуру, которая принимает три числа в качестве аргументов и возвращает сумму квадратов двух больших чисел.Подобную процедуру легко определить, используя встроенные процедуры Scheme square, max, и min, но мы еще не сталкивались с этими процедурами на том этапе книги, поэтому я бы также дал им определение.

(define (square x)
   (* x x))

(define (max x y)
   (if (> x y) x y))

(define (min x y)
   (if (< x y) x y))

(define (sum-of-highest-squares x y z)
   (+ (square (max x y))
      (square (max (min x y) z))))

В sum-of-highest-squares процедура работает путем сложения квадрата максимального значения x и y (максимальное значение этих двух исключается из числа наименьших из трех) и квадрата максимального значения оставшихся двух (минимального значения x и y, которое будет равняться любому значению, оставшемуся после первого шага), и z .

Примечание:Это из моего поста в блоге Упражнения SICP 1.1 - 1.5.Там также есть ссылки, которые приведут вас ко множеству других решений SICP.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top