В C#
Random random = new Random((int)DateTime.Now.Ticks);
OrderedDictionary od = new OrderedDictionary();
od.Add("abc", 1);
od.Add("def", 2);
od.Add("ghi", 3);
od.Add("jkl", 4);
int randomIndex = random.Next(od.Count);
Console.WriteLine(od[randomIndex]);
// Can access via index or key value:
Console.WriteLine(od[1]);
Console.WriteLine(od["def"]);
Интересный вопрос. Кстати, я являюсь автором / сопровождающим phc (компилятор для PHP), и я пишу докторскую диссертацию по компиляторам для динамических языков, поэтому я надеюсь, что смогу предложить некоторые идеи.
Я думаю, что есть ошибочное предположение здесь. Авторы PHP, Perl, Python, Ruby, Lua и т.д. не проектировали «интерпретируемые языки», они разрабатывали динамические языки и реализовывали их с помощью интерпретаторов. Они сделали это, потому что интерпретаторы намного проще писать, чем компиляторы.
Первая реализация Java была интерпретирована, и это статически типизированный язык. Интерпретаторы действительно существуют для статических языков: у Haskell и OCaml есть интерпретаторы, и раньше был популярный интерпретатор для C, но это было очень давно. Они популярны, потому что позволяют REPL , что может облегчить разработку.
Тем не менее, как и следовало ожидать, в сообществе динамических языков существует отвращение к статической типизации. Они считают, что системы статических типов, предоставляемые C, C ++ и Java, многословны и не стоят усилий. Я думаю, что в определенной степени согласен с этим. Программировать на Python гораздо интереснее, чем на C ++.
Обращаясь к мнению других:
dlamblin говорит : «Я никогда особо не чувствовал, что есть что-то особенное в компиляции и интерпретации, предполагающее динамическую типизацию вместо статической. " Что ж, вы очень ошибаетесь. Компиляция динамических языков очень сложна. В основном следует рассмотреть оператор eval
, который широко используется в Javascript и Ruby. phc заранее компилирует PHP, но нам по-прежнему нужен интерпретатор времени выполнения для обработки eval
s. eval
также не может быть проанализирован статически в оптимизирующем компиляторе, хотя есть классный метод , если вам не нужна надежность.
На ответ Дамблина на Эндрю Hare : вы, конечно, можете выполнить статический анализ в интерпретаторе и найти ошибки до времени выполнения, что и делает Haskell ghci
. Я ожидаю, что этого требует стиль интерпретатора, используемый в функциональных языках. dlamblin, конечно, прав, когда говорит, что анализ не является частью интерпретации.
Ответ Эндрю Хара основан на неверном предположении вопрошающего, и точно так же все обстоит не так. Однако он поднимает интересный вопрос: « Также см. Предыдущий пункт.
Наиболее правильный ответ на данный момент - это Иво Ветцель . Однако описываемые им моменты могут быть обработаны во время выполнения в компиляторе, и существует множество компиляторов для Lisp и Scheme, которые имеют этот тип динамической привязки. Но да, это сложно.
Возможно, это потому, что одним из моих основных интерпретируемых языков является Perl, а одним из моих компилируемых языков является Objective-C, но я никогда не чувствовал, что в этом есть что-то особенно в отношении компиляции и интерпретации, которые предлагали динамическую типизацию вместо статической.
Я думаю, ясно, что обе стороны смотрят друг на друга и думают: «В этом есть некоторые преимущества». В некоторых приложениях легче получить некоторую гибкость динамических типов, в то время как может быть проще поддерживать что-то, что статически типизировано и принудительно.
Я не согласен с объяснением Эндрю Хейра . В то время как чисто интерпретируемый язык должен был бы добавить этап предварительной обработки, и, следовательно, не может интерпретироваться исключительно для предупреждения программиста перед выполнением ошибок статической типизации, это не препятствует выдаче ошибки типа во время выполнения, когда она возникает. Таким образом, отсутствие компиляции не означает, что проверка статического типа невозможна. Но поскольку получение ошибки типа во время выполнения не так полезно, как ее получение при компиляции или во время предполетной проверки, я могу видеть, как «преимущество» статической типизации в этой ситуации может показаться более неприятным, и, таким образом, вас выбрасывают в пользу преимуществ, которые дает динамическая типизация.
Если бы вы знали с самого начала, что предпочитаете сохранять свои типы статическими, потому что лично вы в результате пишете лучше, более удобный в сопровождении код, и вы проектировали свой интерпретируемый язык , ничто не должно мешать вам проектировать язык как статически типизированный. Есть приличная вики-статья , посвященная только типированию.
В интерпретируемых языках используется динамическая типизация, потому что нет этапа компиляции, на котором можно было бы проводить статический анализ. Скомпилированные языки выполняют статический анализ во время компиляции , что означает, что любые ошибки типа сообщаются разработчику по мере их работы.
Это легче понять, если учесть, что у статически типизированного языка есть компилятор, который обеспечивает выполнение правила типов вне контекста исполнения. Интерпретируемые языки никогда не анализируются статически, поэтому правила типов должны выполняться интерпретатором в контексте выполнения.
Я думаю, это из-за природы интерпретируемых языков, они хотят быть динамическими, поэтому вы МОЖЕТЕ изменить вещи во время выполнения. Из-за этого компилятор никогда точно не знает, в каком состоянии программа после того, как следующая строка кода была исключена.
Представьте себе следующий сценарий (на Python):
import random
foo = 1
def doSomeStuffWithFoo():
global foo
foo = random.randint(0, 1)
def asign():
global foo
if foo == 1:
return 20
else:
return "Test"
def toBeStaticallyAnalyzed():
myValue = asign()
# A "Compiler" may throw an error here because foo == 0, but at runtime foo maybe 1, so the compiler would be wrong with its assumption
myValue += 20
doSomeStuffWithFoo() # Foo could be 1 or 0 now... or 4 ;)
toBeStaticallyAnalyzed()
Как вы можете надеяться, компилятор не имеют смысл в этой ситуации. На самом деле он может предупредить вас о возможности того, что myValue может быть чем-то другим, кроме Number. Но тогда в JavaScript это не удастся, потому что, если myValue является String, 20 также будет неявно преобразован в String, поэтому ошибки не произойдет. Таким образом, вы можете получать тысячи бесполезных предупреждений повсюду, и я не думаю, что это предназначено для компилятора.
Гибкость всегда имеет свою цену, вам нужно глубже взглянуть на свою программу, или запрограммируйте его более тщательно, другими словами, вы являетесь КОМПИЛЯТОРОМ в подобных ситуациях.
Итак, ваше решение в качестве компилятора? - Исправьте это с помощью "попробуйте: except":)
Потому что стандарт C так говорит, и он получает единственный голос.
И стандарт, вероятно, правильный, потому что sizeof
принимает тип и
В целом , если домен или codomain (или оба) функции содержат элементы, значительно более сложные, чем действительные числа, эта функция называется оператором. И наоборот, если ни область, ни область значений функции не содержат элементов более сложных, чем действительные числа, эта функция, вероятно, будет называться просто функцией. Тригонометрические функции, такие как косинус, являются примерами последнего случая.
Кроме того, когда функции используются так часто, что они развиваются быстрее или легче, чем общая форма F (x, y, z, ...), в результате специальные формы также называются операторами. Примеры включают инфиксные операторы, такие как сложение "
Вероятно, у вас может быть язык, на котором вы должны явно объявлять тип каждой переменной, но если вы этого не сделаете, гораздо проще делать интересные вещи, которые при статической типизации потребовали бы от программиста очень тщательно созданных сложных универсальных типов.
С другой стороны. Знаете ли вы какой-нибудь динамически типизированный компилируемый (статически, не JIT) язык?
Компиляторы + статические типы = эффективный машинный код
Компиляторы + динамические типы = неэффективный машинный код
Рассмотрим следующий псевдокод:
function foo(a, b) {
return a+b
}
Статический язык сможет узнать (посредством объявления или вывода), что a и b являются целыми числами, и будет компилироваться до
%reg = addi a,b
или чего-то подобного. во всяком случае аналогично.
Компилятор для динамического языка должен был бы передать код в
1. Проверьте типы a и b
2. обрабатывать каждый случай или комбинацию случаев
%reg1 = typeof a
beq %reg1, int, a_int_case
beq %reg1, float, a_float_case
beq %reg1, string, a_string_case
label a_int_case
%reg1 = typeof b
beq %reg1, int, a_int_b_int_case
beq %reg1, float, a_int_b_float_case
beq %reg1, string, a_int_b_string_case
label a_int_b_int_case
%out = addi a,b
goto done
label a_int_b_float_case
%tmp = mkfloat a
%out = addf %tmp,b
goto done
... Etc. I can't finish
Хотя вы могли бы сгенерировать более умный машинный код, чем этот, вы не сможете помочь с генерацией большого количества кода - это делает компиляцию не основным преимуществом для динамического языка.
] Поскольку интерпретаторы писать намного проще, а компиляция не приносит много пользы, почему бы не написать интерпретатор?
(Оперативные компиляторы действительно имеют информацию о типе и могут компилироваться вплоть до одного оператора. На самом деле они содержат больше информации, чем системы статического типа, и теоретически могут работать даже лучше. Весь ассемблер смоделирован; Любое сходство с реальным кодом, который мог бы работать на реальной машине, является чисто случайным.)