Ранние версии JavaScript не позволили названные выражения function, и из-за этого мы не могли сделать выражение рекурсивной функции:
// This snippet will work:
function factorial(n) {
return (!(n>1))? 1 : factorial(n-1)*n;
}
[1,2,3,4,5].map(factorial);
// But this snippet will not:
[1,2,3,4,5].map(function(n) {
return (!(n>1))? 1 : /* what goes here? */ (n-1)*n;
});
Для двигений это, arguments.callee
было добавлено так, мы могли сделать:
[1,2,3,4,5].map(function(n) {
return (!(n>1))? 1 : arguments.callee(n-1)*n;
});
Однако это было на самом деле действительно плохим решением, поскольку это (в сочетании с другими аргументами, вызываемым и проблемами вызывающей стороны) делает встраивание и хвостовую рекурсию невозможными в общем случае (можно достигнуть его в избранных случаях посредством трассировки и т.д., но даже лучший код sub оптимальный из-за проверок, которые иначе не были бы необходимы). Другая главная проблема - то, что рекурсивный вызов получит различное this
значение, например:
var global = this;
var sillyFunction = function (recursed) {
if (!recursed)
return arguments.callee(true);
if (this !== global)
alert("This is: " + this);
else
alert("This is the global");
}
sillyFunction();
Во всяком случае, EcmaScript 3 решил эти вопросы путем разрешения названный выражениями function, например:
[1,2,3,4,5].map(function factorial(n) {
return (!(n>1))? 1 : factorial(n-1)*n;
});
Это обладает многочисленными преимуществами:
функция может быть вызвана как любой другой из Вашего кода.
Это не загрязняет пространство имен.
значение this
не изменяется.
Это более производительно (доступ , объект arguments является дорогим).
Просто поняли, что в дополнение ко всему остальному вопрос был [приблизительно 118], или более конкретно Function.caller
.
В любом моменте времени можно найти самую глубокую вызывающую сторону любой функции на стеке, и как я сказал выше, смотрение на стек вызовов имеет один единственный главный эффект: Это делает большое количество из оптимизации невозможным, или намного намного более трудным.
, Например, если мы не можем гарантировать, что функция f
не вызовет неизвестную функцию, тогда не возможно встроить f
. В основном это означает, что любой сайт вызова, который, возможно, был тривиально inlinable, накапливает большое количество защиты, возьмите:
function f(a, b, c, d, e) { return a ? b * c : d * e; }
, Если js интерпретатор не может гарантировать, что всеми обеспеченными аргументами являются числа в точке, что вызов выполняется, он должен или вставить проверки на все аргументы перед встроенным кодом, или он не может встроить функцию.
Теперь в данном случае умный интерпретатор должен быть в состоянии перестроить проверки, чтобы быть более оптимальным и не проверить любые значения, которые не использовались бы. Однако во многих случаях это просто не возможно, и поэтому становится невозможно встроить.
если вы хотите, чтобы последовательность случайных чисел изменялась каждый раз, когда вы запускаете свою программу, вам необходимо изменить случайное начальное число, инициализировав его текущим временем, например
, вы найдете пример там , выдержка:
/*
* Change seed to something else.
*
* Caveat: std::time(0) is not a very good truly-random seed. When
* called in rapid succession, it could return the same values, and
* thus the same random number sequences could ensue. If not the same
* values are returned, the values differ only slightly in the
* lowest bits. A linear congruential generator with a small factor
* wrapped in a uniform_smallint (see experiment) will produce the same
* values for the first few iterations. This is because uniform_smallint
* takes only the highest bits of the generator, and the generator itself
* needs a few iterations to spread the initial entropy from the lowest bits
* to the whole state.
*/
generator.seed(static_cast<unsigned int>(std::time(0)));
Вам нужно заполнить генератор случайных чисел, чтобы он не запускался каждый раз с одного и того же места.
В зависимости от того, что вы делаете с числами, вам может потребоваться добавить некоторые подумал о том, как выбрать начальную ценность. Если вам нужна высококачественная случайность (если вы генерируете криптографические ключи и хотите, чтобы они были достаточно безопасными), вам понадобится хорошее начальное значение. Если бы это был Posix, я бы предложил / dev / random - но вы рассчитываете использовать Windows, поэтому я не уверен, какой был бы хороший исходный источник.
Но если вы не возражаете против предсказуемого начального числа (для игр , моделирование и т. д.), быстрое и грязное начальное число - это текущая отметка времени, возвращаемая функцией time ().
Если вы работаете в системе 'nix, вы всегда можете попробовать что-то вроде этого;
int getSeed()
{
ifstream rand("/dev/urandom");
char tmp[sizeof(int)];
rand.read(tmp,sizeof(int));
rand.close();
int* number = reinterpret_cast<int*>(tmp);
return (*number);
}
Я предполагаю, что заполнение генератора случайных чисел таким способом быстрее, чем просто чтение / dev / urandom
(или / dev / random
) для всех ваших потребностей в случайных числах.