Использовать функцию внутри функции

#racket

#ракетка

Вопрос:

У меня есть вызываемая функция clean-up , которая в основном выполняет то, что делает уже доступная flatten функция. Затем у меня вызывается функция multiplier , которая принимает список и умножает все числа внутри него. Одна проблема заключается в том, что иногда может быть странный синтаксис для списка, используемого в multiplier , и он не умножает каждое число вместе. Например:

Пример ввода

 (multiplier '((1 (2 3)) 4 5 (6)))
  

Корректный вывод

 720
  

Мой вывод

 *: contract violation
  expected: number?
  given: '(6 . 1)
  argument position: 2nd
  other arguments...
  

Теперь нам не нравятся ошибки, не так ли? Эта multiplier функция работает в обычном списке, что-то вроде (multiplier '(1 2 3 4 5 6)) . Итак, я написал clean-up функцию, чтобы превратить некоторый запутанный список в обычный список. Однако я не знаю, как вызвать ее, чтобы очистить мой список, прежде чем пытаться выполнить синтаксический анализ и выполнить умножение. Я могу убедиться, что clean-up функция отлично выполняет свою работу. Кто-нибудь может помочь? Вот код, который у меня есть для обоих:

 (define (clean-up s)
  (cond [(null? s) '()]
        [(not (pair? s)) (list s)]
        [else (append (clean-up (car s)) (clean-up (cdr s)))]
))

(define multiplier
  (lambda (s)
    (cond [(null? s) 1]
          [(number? (car s)) (* (car s) (multiplier(cdr s)))]
          [list? (car s) (append (car s) (multiplier(cdr s)))]
          [else (multiplier (cdr s))]
)))

  

Ответ №1:

Нет необходимости в clean-up функции для решения проблем, возникающих в multiplier . Тем не менее, вы могли бы сначала вызвать clean-up ввод просто с помощью:

 scratch.rkt> (multiplier (clean-up '((1 (2 3)) 4 5 (6))))
720
  

Или вы можете создать вспомогательную функцию, чтобы сделать это за вас:

 (define (multiplier-workaround s)
  (multiplier (clean-up s)))
  
 scratch.rkt> (multiplier-workaround '((1 (2 3)) 4 5 (6)))
720
  

Подобный обходной путь может помочь вам в крайнем случае, но он не устраняет реальные проблемы в коде.

Некоторые из проблем здесь может быть легче увидеть с лучшим форматированием кода. Оставлять висячие круглые скобки в Lisps — плохой стиль, как если бы это были языки в стиле C; но это, вероятно, не вызывает у вас проблем здесь. Тем не менее, это хороший стиль, чтобы показать структуру ваших выражений с помощью отступов. При правильном отступе становится очевидным, что в строке с предикатом отсутствуют круглые скобки list? :

 (define multiplier
  (lambda (s)
    (cond [(null? s) 1]
          [(number? (car s))
           (* (car s) (multiplier(cdr s)))]
          [list? (car s)
                 (append (car s) (multiplier (cdr s)))]
          [else
           (multiplier (cdr s))])))
  

Дальнейшее изучение этой строки показывает, что код пытается добавить (car s) к результату (multiplier (cdr s)) ; тем не менее, (car s) теперь известно, что это список, и multiplier предполагается, что он возвращает число! Целью здесь, несомненно, было умножить результат вызова multiply списка (car s) вместе с результатом (multiplier (cdr s)) :

 (define multiplier
  (lambda (s)
    (cond [(null? s) 1]
          [(number? (car s))
           (* (car s)
              (multiplier (cdr s)))]
          [(list? (car s))
           (* (multiplier (car s))
              (multiplier (cdr s)))]
          [else
           (multiplier (cdr s))])))
  

Неясно, зачем else нужна ветка, если только OP не хочет иметь возможность обрабатывать такие списки, как (a (1 (2 b) (3 (c (4 5) 6) d) e))) . Для кода, который ожидает вложенные списки чисел, это было бы хорошо:

 (define multiplier-2
  (lambda (s)
    (cond [(null? s) 1]
          [(number? (car s))
           (* (car s) (multiplier (cdr s)))]
          [else
           (* (multiplier (car s))
              (multiplier (cdr s)))])))
  

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

 scratch.rkt> (multiplier '((1 (2 3)) 4 5 (6)))
720
scratch.rkt> (multiplier-2 '((1 (2 3)) 4 5 (6)))
720
scratch.rkt> (multiplier '(a (1 (2 3)) 4 5 b (6) c))
720
  

Ответ №2:

Я думаю, что у buildin flatten больше обработки ошибок.

 (define (multiplier lst)
  (apply * (filter number? (flatten lst))))

;;; TEST
(define test-lst1 (list   (vector 1) '(1 (2 3) c) "w" 4 5 '(6)   - * sqr))
(define test-lst2 '(1(a 2(3 b (c 4(d 5 e(6) (f)))))))
(multiplier test-lst1) ; 720
(multiplier test-lst2) ; 720