Наблюдение ответов здесь, я думаю, что мы можем прийти к заключению, что большинство из нас действительно приближает порядок алгоритма взгляд в нем и использует здравый смысл вместо того, чтобы вычислить его с, например, основной метод , поскольку о нас думали в университете. После этих слов я должен добавить, что даже преподаватель призвал нас (позже) к на самом деле , думают об этом вместо того, чтобы просто вычислить его.
Также я хотел бы добавить, как это сделано для [1 122] рекурсивные функции :
предполагают, что у нас есть функция как ( код схемы ):
(define (fac n)
(if (= n 0)
1
(* n (fac (- n 1)))))
, который рекурсивно вычисляет факториал данного числа.
первый шаг должен попытаться определить рабочую характеристику для тело функции [только 118] в этом случае, ничто специальное не сделано в теле, просто умножение (или возврат значения 1).
Так производительность для тела: O (1) (постоянный).
Следующий пытаются определить это для количество рекурсивных вызовов . В этом случае у нас есть n-1 рекурсивные вызовы.
Так производительность для рекурсивных вызовов: O (n-1) (порядок является n, поскольку мы выбрасываем незначительные части).
Тогда соединяет те два, и у Вас тогда есть производительность для целой рекурсивной функции:
1 * (n-1) = O (n)
Peter, для ответа поднятые вопросы; метод, который я описываю здесь на самом деле, обрабатывает это вполне хорошо. Но имейте в виду, что это все еще приближение и не полный математически корректный ответ. Метод, описанный здесь, является также одним из методов, которые нам преподавали в университете, и если я помню, правильно использовался для намного более усовершенствованных алгоритмов, чем факториал, который я использовал в этом примере.
, Конечно, все это зависит от того, как хорошо можно оценить время выполнения тела функции и количества рекурсивных вызовов, но это столь же верно для других методов.
Короткий ответ заключается в том, что правоассоциативность может улучшить читаемость, сделав тип программиста совместимым с тем, что программа фактически делает.
Итак, если вы наберете ' 1 :: 2 :: 3
', вы получите обратно список (1, 2, 3) вместо того, чтобы возвращать список в совершенно другом порядке.
Это потому, что « 1 :: 2 :: 3 :: Nil
» на самом деле
List[Int].3.prepend(2).prepend(1)
scala> 1 :: 2 :: 3:: Nil
res0: List[Int] = List(1, 2, 3)
, что одновременно:
prepend
, vs. O (n) для гипотетического метода добавления
) (Напоминание, выдержка из книги Программирование на Scala )
Если метод используется в обозначении оператора, например a * b
, метод вызывается для левого операнда, как в a. * (B)
- если имя метода не заканчивается в двоеточие.
Если имя метода заканчивается двоеточием, метод вызывается для правого операнда.
Следовательно, в 1 :: twoThree
метод ::
вызывается для twoThree
, передавая 1, например: twoThree .: :( 1)
.
Для List он играет роль операции добавления (кажется, что список добавляется после «1», образуя « 1 2 3
», где на самом деле это равно 1, который добавлен к списку ).
Список классов не предлагает настоящую операцию добавления, потому что время, необходимое для добавления в список, растет линейно с размером списка, тогда как добавление с :: требует постоянного времени .
myList :: 1
попытается добавить все содержимое myList к '1', что будет длиннее, чем добавление 1 к myList (как в ' 1 :: myList
')
Примечание: неважно какую ассоциативность имеет оператор, однако его операнды
всегда оценивается слева направо.
Итак, если b - это выражение, которое не является простой ссылкой на неизменяемое значение, тогда a ::: b более точно рассматривается как следующий блок:
{ val x = a; b.:::(x) }
В этом блоке a по-прежнему оценивается до b, а затем результат этой оценки передается как операнд методу b :::.
зачем вообще различать левоассоциативные и правоассоциативные методы?
Это позволяет сохранить вид обычной левоассоциативной операции (' 1 :: myList
'), фактически применяя операцию к правильному выражению, потому что;
1 :: myList
' vs. ' myList.prepend (1)
') Итак, как вы говорите , "синтаксический сахар", насколько мне известно.
Обратите внимание, что в случае foldLeft
, например, они могли зайти слишком далеко (с эквивалентом правоассоциативного оператора « /:
»)
Чтобы включить некоторые из ваших комментариев, слегка перефразируя:
если вы рассматриваете левоассоциативную функцию 'append', то вы должны написать ' oneTwo append 3 append 4 append 5
'.
Однако, если бы к oneTwo добавить 3, 4 и 5 (что можно было бы предположить по тому, как это написано), получилось бы O (N).
То же самое с '::', если это было для "добавления". Но это не так. На самом деле это для "prepend"
. Это означает, что ' a :: b :: Nil
' для ' List []. B.prepend (a)
'
Если бы "::" добавлялось в начало и все же оставалось левоассоциативным, то результирующий список был бы в неправильном порядке.
Можно было бы ожидать, что он вернет List (1, 2, 3, 4, 5), но в итоге он вернет List (5, 4, 3, 1, 2), что может быть неожиданным для программиста.
Это потому, что то, что вы сделали, было бы в левоассоциативном порядке:
(1,2).prepend(3).prepend(4).prepend(5) : (5,4,3,1,2)
Итак, правоассоциативность заставляет код совпадать с фактическим порядком возвращаемого значения.
В чем смысл возможности определять правоассоциативные методы?
Я думаю, что суть правоассоциативного метода состоит в том, чтобы дать кому-то возможность расширить язык, а именно точка переопределения оператора в целом.
Перегрузка оператора - полезная вещь, поэтому Scala сказал: почему бы не открыть ее для любой комбинации символов? Скорее, зачем проводить различие между операторами и методами? Теперь, как разработчик библиотеки взаимодействует со встроенными типами, такими как Int
? В C ++ она использовала бы функцию friend
в глобальной области видимости. Что, если мы хотим, чтобы все типы реализовывали оператор ::
?
Правоассоциативность дает чистый способ добавить оператор ::
ко всем типам . Конечно, технически :: Оператор
- это метод типа List. Но это также выдуманный оператор для встроенного Int и всех других типов, по крайней мере, если вы можете игнорировать :: Nil
в конце.
I Думаю, это отражает философию Scala по реализации как можно большего количества вещей в библиотеке и обеспечению гибкости языка для их поддержки. Это дает возможность кому-то придумать SuperList, который можно назвать:
1 :: 2 :: SuperNil
Несколько прискорбно, что правая ассоциативность в настоящее время жестко запрограммирована только для двоеточия в конце, но я думаю, что это позволяет достаточно легко помните.
философия реализации как можно большего количества вещей в библиотеке и создания гибкого языка для их поддержки. Это дает возможность кому-то придумать SuperList, который можно назвать:1 :: 2 :: SuperNil
Несколько прискорбно, что правая ассоциативность в настоящее время жестко запрограммирована только для двоеточия в конце, но я думаю, что это позволяет достаточно легко помните.
философия реализации как можно большего количества вещей в библиотеке и создания гибкого языка для их поддержки. Это дает возможность кому-то придумать SuperList, который можно было бы назвать так:1 :: 2 :: SuperNil
К сожалению, правая ассоциативность в настоящее время жестко запрограммирована только для двоеточия в конце, но я думаю, что это позволяет достаточно легко помните.