Почему мы не можем переопределить хвостовые рекурсивные методы? [Дубликат]

Как я объясняю в мой ответ на другой вопрос, PECS - это мнемоническое устройство, созданное Джошем Блохом, чтобы помочь вспомнить производителя extends, Consumer super.

Это означает, что когда параметризованный тип, передаваемый методу, будет выдавать экземпляры из T (они будут извлечены из него каким-либо образом), следует использовать ? extends T, поскольку любой экземпляр подкласса T также является T.

Когда параметризованный тип, передаваемый методу, будет потреблять экземпляры T (они будут переданы в он должен что-то сделать), ? super T следует использовать, потому что экземпляр T можно законно передать любому методу, который принимает некоторый супертип T. Например, Comparator можно использовать на Collection. ? extends T не будет работать, потому что Comparator не может работать на Collection.

blockquote>

Обратите внимание, что обычно вы должны использовать только ? extends T и ? super T для параметров какого-либо метода. Методы должны использовать T только как параметр типа для типичного типа возврата.

44
задан Peter Mortensen 2 May 2013 в 17:50
поделиться

4 ответа

Рассмотрим следующее взаимодействие с REPL. Сначала мы определяем класс с факториальным методом:

scala> class C {
         def fact(n: Int, result: Int): Int =
           if(n == 0) result
           else fact(n - 1, n * result)
       }
defined class C

scala> (new C).fact(5, 1)
res11: Int = 120

Теперь давайте переопределим его в подклассе, чтобы удвоить ответ суперкласса:

scala> class C2 extends C {
         override def fact(n: Int, result: Int): Int = 2 * super.fact(n, result)
       }
defined class C2

scala> (new C).fact(5, 1)
res12: Int = 120

scala> (new C2).fact(5, 1)

Какой результат вы ожидаете для этого последний звонок? Вы можете ожидать 240. Но нет:

scala> (new C2).fact(5, 1)
res13: Int = 7680

Это потому, что, когда метод суперкласса делает рекурсивный вызов, рекурсивный вызов проходит через подкласс.

Если переопределение работает так, что 240 был правильным ответом, тогда было бы безопасно, чтобы оптимизация хвостового вызова выполнялась в суперклассе. Но это не так, как работает Scala (или Java).

Если метод не помечен как final, он может не вызывать себя , когда он делает рекурсивный вызов.

И вот почему @tailrec не работает, если метод не является окончательным (или закрытым).

UPDATE: Я рекомендую прочитать другие два ответа (John's and Rex's).

55
ответ дан Seth Tisue 18 August 2018 в 06:00
поделиться
  • 1
    спасибо amacleod, drdozer и другие на #scala – Seth Tisue 24 January 2011 в 19:27
  • 2
    Отличное объяснение! – soc 25 January 2011 в 11:45
  • 3
    Возможно, стоит сказать, что «может и не называть себя». является специфической для Scala проблемой, которая не имеет ничего общего с устранением хвостового вызова в целом. Все эти хвостовые вызовы будут устранены в SML, OCaml, F # и т. Д. – Jon Harrop 31 January 2012 в 17:49
  • 4
    – Kevin Meredith 16 April 2014 в 03:51
  • 5
    @KevinMeredith: Я думаю, что это создало бы большой отдельный вопрос. – Seth Tisue 16 April 2014 в 04:15

Что именно пойдет не так, если компилятор применил TCO в таком случае?

Ничто не пойдет не так. Любой язык с правильным устранением вызова хвоста сделает это (SML, OCaml, F #, Haskell и т. Д.). Единственная причина, по которой Scala не заключается в том, что JVM не поддерживает хвостовую рекурсию, и обычный взлом Scala для замены саморекурсивных вызовов в хвостовой позиции с goto не работает в этом случае. Scala в CLR может сделать это, как F #.

0
ответ дан Jon Harrop 18 August 2018 в 06:00
поделиться
  • 1
    hey mate вы можете мне сказать, почему f # взрывается в этом случае ideone.com/VgJnR6 - моно на идеоне не удается с помощью сигсегва и tryfsharp в авариях win8 IE – OlegYch 13 August 2013 в 02:06
  • 2
    @OlegYch Mono и TryFSharp не выполняют надлежащего устранения хвостового вызова. Ваша программа отлично работает в .NET при включенном TCO. – Jon Harrop 13 August 2013 в 14:52
  • 3
    Спасибо, Джон, оцените ваши отзывы! – OlegYch 13 August 2013 в 15:58
23
ответ дан Rex Kerr 18 August 2018 в 06:00
поделиться
7
ответ дан Shawn Mehan 18 August 2018 в 06:00
поделиться
Другие вопросы по тегам:

Похожие вопросы: