Почему вызов моей домашней функции (reduce) с помощью макроса влияет на будущие вызовы этой функции?

Предыстория: несколько недель назад я работал над проектом в схеме guile 1.8.8 и, немного заржавев, забыл о встроенной функции (reduce), поэтому Я свой закатал. Чуть позже я столкнулся с, казалось бы, безнадежной ошибкой, когда вызов функции без побочных эффектов изменил ход остальной части программы (работает правильно и проходит несколько модульных тестов вместо полного сбоя) ПОСЛЕ того, как функция давно вернулся. Несколько фрагментов кода, которые раньше возвращали что-то вроде (A B C D), теперь возвращали только (A), вызывая множество проблем.

Минимальный рабочий пример: после нескольких дней урезания я загнал проблему в этот небольшой фрагмент автономного кода:

(define (my-reduce fun ls)
    (if (null? (cdr ls))
        (car ls)
        (my-reduce fun (cons (fun (car ls) (cadr ls))
                             (cddr ls)))))

(format #t "~a "  (my-reduce +   '(1 2 3)))
(format #t "~a "  (my-reduce or  '(1 2 3)))
(format #t "~a~%" (my-reduce +   '(1 2 3)))

Что выводит 6 1 1 вместо ожидаемого 6 1 6.

Дополнительные наблюдения:

  • Установка во второй строке значения + дает ожидаемое значение 6 6 6.
  • Установка второй строки на and дает 6 3 3.
  • Дополнительные строки + после них производят дополнительные 1s или 3s в зависимости от того, что установлено во второй строке. Итак, последовательность + or + + приводит к выходу 6 1 1 1.
  • Дополнительные строки and или or после первой НЕ переключают вывод обратно. Итак, если последовательность + and or +, выход 6 3 3 3. Кажется, что как только я передал or или and в (my-reduce), функция навсегда "застревает", имея это в качестве аргумента.
  • Я также заметил, что передача and или or встроенной функции (reduce) вызывает ошибку типа, поскольку технически они являются макросами, а не функциями.
  • В том же духе я замечаю, что замена or на (lambda (x y) (or x y)) дает ожидаемый результат. Таким образом, кажется, что критическим моментом здесь является то, что передача макросов в мою домашнюю функцию сокращения вызывает проблему.

Вопрос. Что здесь происходит? Почему вызов (my-reduce) с помощью and или or вызывает такое неожиданное поведение? Связано ли это с тем, что эти «функции» на самом деле являются макросами?

Заранее благодарю за любую помощь. Этот меня действительно поразил!


person MegaWidget    schedule 03.09.2018    source источник


Ответы (1)


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

(my-reduce or '(1 2 3))

Единственный способ, который я могу придумать, это то, что вы где-то переопределили and, or как функции, и это является источником проблем. В качестве примечания, my-reduce (понимаемый как операция сгиба вправо) может быть реализован более стандартным способом, например:

(define (my-reduce fun ls)
  (if (null? (cdr ls))
      (car ls)
      (fun (car ls)
           (my-reduce fun (cdr ls)))))

Вышеприведенное предполагает, что список не пуст, если это не всегда так, обычно в качестве параметра передается начальное значение:

(define (my-reduce fun init ls)
  (if (null? ls)
      init
      (fun (car ls)
           (my-reduce fun init (cdr ls)))))
person Óscar López    schedule 03.09.2018
comment
Спасибо за ответ. Я реализовал my-reduce как левоассоциативный, потому что в соответствии с эта страница кажется стандартной для того, чтобы только правая ассоциативная страница была явно помечена как таковая. И да, я знал, что он предполагал непустой список, это был своего рода хак, так как я знал, что никогда не передам пустой список. - person MegaWidget; 03.09.2018
comment
Теперь уточняющий вопрос: вы говорите, что передача макроса там, где ожидается функция, приводит к неопределенному поведению? Если да, не могли бы вы связать соответствующую документацию? В противном случае я не думаю, что смогу дать этому зеленый чек, поскольку он не дает полного ответа. Почему вызов (my-reduce) с и или или вызывает такое неожиданное поведение? - person MegaWidget; 03.09.2018
comment
(Кроме того, я не переопределял and и or, я запускаю код точно так, как он отображается в MWE. Не произошел ли сбой в вашей реализации? Казалось бы, это указывает на неопределенное поведение.) - person MegaWidget; 03.09.2018
comment
Это не неопределенное или неожиданное поведение, просто вы пытаетесь передать неправильный тип объекта, поэтому возникает ошибка. my-reduce ожидает функцию в качестве параметра, макросы не являются функциями - они имеют другие правила вычисления и не могут передаваться так же, как функция. - person Óscar López; 03.09.2018
comment
В моем интерпретаторе (DrRacket) my-reduce дает сбой перед выполнением, когда обнаруживает, что or передается в качестве параметра. Он даже никогда не печатает 1. Моя точка зрения: в вашем my-reduce нет ничего плохого (кроме странной реализации, которая создает список, когда этого не должно быть). - person Óscar López; 03.09.2018
comment
Ничто в опубликованном вами коде не объясняет, почему результаты меняются между вызовами, я даже тестировал его в Guile, и неудивительно, что я получаю сообщение об ошибке при попытке передать or в качестве параметра: "source expression failed to match any pattern in form or". Так что определенно есть что-то другое, специфичное для вашей среды, что делает невозможным воспроизведение проблемы. - person Óscar López; 03.09.2018
comment
Хорошо, спасибо за помощь. Из любопытства, какую версию хитрости вы используете? У меня стоит 1.8.8. Возможно, это ошибка интерпретатора. В любом случае: it's just that you're trying to pass the wrong type of object, hence an error is raised. Ошибки не возникает, код работает чисто. Поэтому я ожидаю, что в принципе возможен более полный ответ - тот, который ссылается на документ и говорит, что должно происходить, когда вы передаете макрос, где ожидается функция. Было бы приемлемым этикетом сайта оставить вопрос открытым на некоторое время, чтобы посмотреть, появится ли такой ответ? - person MegaWidget; 03.09.2018
comment
Я использую Guile 2.2.4, посмотрите, решит ли обновление до более новой версии ваши проблемы :). Потому что то, что вы делаете, должно вызвать ошибку, это точно. Сомневаюсь, что это указано в документации, но я, конечно, могу ошибаться :) . Можно подождать пару дней, пока появится лучший ответ, просто не забудьте прийти и проверить его. - person Óscar López; 03.09.2018
comment
Ах, я не знал, что я так сильно отстал от последней версии, я бы обновился раньше! Думаю, я пролистаю документацию, чтобы посмотреть, что там говорится об этом. Опять же, даже если это ошибка, если это не самая последняя версия, это не имеет большого значения, так что, возможно, я просто поверю вам на слово :P Еще раз спасибо! :) - person MegaWidget; 03.09.2018