Почему реквизит, не работающий с рендером, как функция толстой стрелки [дубликат]

Основная (языковая агностическая) причина

Поскольку не все числа могут быть представлены точно в IEEE с плавающей точкой арифметики (стандарт, который почти все компьютеры используют для представления десятичных чисел и делают математика с ними), вы не всегда получите то, что ожидаете. Это особенно верно, потому что некоторые значения, которые являются простыми, конечными десятичными знаками (такими как 0,1 и 0,05), не представлены точно в компьютере, и поэтому результаты арифметики на них могут не дать результата, который идентичен прямому представлению " (g30) Это хорошо известное ограничение компьютерной арифметики и обсуждается в нескольких местах:

Сравнение скаляров

Стандартное решение этого в R не должно использовать == , а скорее функцию all.equal . Вернее, поскольку all.equal дает много подробностей о различиях, если они есть, isTRUE(all.equal(...)).

if(isTRUE(all.equal(i,0.15))) cat("i equals 0.15") else cat("i does not equal 0.15")

дает

i equals 0.15

. Еще несколько примеров использования all.equal вместо == (последний пример должен показать, что это будет правильно отображать различия).

0.1+0.05==0.15
#[1] FALSE
isTRUE(all.equal(0.1+0.05, 0.15))
#[1] TRUE
1-0.1-0.1-0.1==0.7
#[1] FALSE
isTRUE(all.equal(1-0.1-0.1-0.1, 0.7))
#[1] TRUE
0.3/0.1 == 3
#[1] FALSE
isTRUE(all.equal(0.3/0.1, 3))
#[1] TRUE
0.1+0.1==0.15
#[1] FALSE
isTRUE(all.equal(0.1+0.1, 0.15))
#[1] FALSE

Более подробная информация, непосредственно скопированная из ответа на аналогичный вопрос :

Проблема, с которой вы столкнулись, заключается в том, что в большинстве случаев плавающая точка не может представлять десятичные дроби, что означает, что вы часто обнаруживаете, что точные совпадения не срабатывают.

немного, когда вы говорите:

1.1-0.2
#[1] 0.9
0.9
#[1] 0.9

Вы можете узнать, что он на самом деле думает в десятичной форме:

sprintf("%.54f",1.1-0.2)
#[1] "0.900000000000000133226762955018784850835800170898437500"
sprintf("%.54f",0.9)
#[1] "0.900000000000000022204460492503130808472633361816406250"

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

sprintf("%a",0.9)
#[1] "0x1.ccccccccccccdp-1"
sprintf("%a",1.1-0.2)
#[1] "0x1.ccccccccccccep-1"
sprintf("%a",1.1-0.2-0.9)
#[1] "0x1p-53"

Вы можете видеть, что они отличаются 2^-53, что важно, потому что это число наименьшая представимая разница между двумя числами, значение которых близко к 1.

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

 ?.Machine
 #....
 #double.eps     the smallest positive floating-point number x 
 #such that 1 + x != 1. It equals base^ulp.digits if either 
 #base is 2 or rounding is 0; otherwise, it is 
 #(base^ulp.digits) / 2. Normally 2.220446e-16.
 #....
 .Machine$double.eps
 #[1] 2.220446e-16
 sprintf("%a",.Machine$double.eps)
 #[1] "0x1p-52"

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

?all.equal
#....
#all.equal(x,y) is a utility to compare R objects x and y testing ‘near equality’.
#....
#all.equal(target, current,
#      tolerance = .Machine$double.eps ^ 0.5,
#      scale = NULL, check.attributes = TRUE, ...)
#....

Таким образом, функция all.equal фактически проверяет, что разница между числами является квадратным корнем из наименьшей разницы между двумя мантиссами.

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

Сравнение векторов

Вышеприведенное обсуждение предполагало сравнение двух отдельных значений. В R нет скаляров, просто векторы и неявная векторизация - сила языка. Для сравнения значений векторов по-прежнему соблюдаются предыдущие принципы, но реализация несколько отличается. == векторизован (имеет элементное сравнение), а all.equal сравнивает целые векторы как единый объект.

Используя предыдущие примеры

a <- c(0.1+0.05, 1-0.1-0.1-0.1, 0.3/0.1, 0.1+0.1)
b <- c(0.15,     0.7,           3,       0.15)

== не дает «ожидаемого» результата, а all.equal не выполняет элементарные

a==b
#[1] FALSE FALSE FALSE FALSE
all.equal(a,b)
#[1] "Mean relative difference: 0.01234568"
isTRUE(all.equal(a,b))
#[1] FALSE

Скорее, следует использовать версию, которая пересекает два вектора

mapply(function(x, y) {isTRUE(all.equal(x, y))}, a, b)
#[1]  TRUE  TRUE  TRUE FALSE

Если требуется функциональная версия, это можно записать

elementwise.all.equal <- Vectorize(function(x, y) {isTRUE(all.equal(x, y))})

, который можно назвать только

elementwise.all.equal(a, b)
#[1]  TRUE  TRUE  TRUE FALSE

. Вместо этого вместо обертывания all.equal в четном больше вызовов функций, вы можете просто воспроизвести соответствующие внутренние элементы all.equal.numeric и использовать неявную векторию:

tolerance = .Machine$double.eps^0.5
# this is the default tolerance used in all.equal,
# but you can pick a different tolerance to match your needs

abs(a - b) < tolerance
#[1]  TRUE  TRUE  TRUE FALSE
253
задан Felix Kling 30 March 2016 в 20:05
поделиться

2 ответа

tl; dr: Нет! Функции стрелок и декларации функций / выражения не являются эквивалентными и не могут быть заменены вслепую. Если функция, которую вы хотите заменить, not использует this, arguments и не вызывается с new, тогда да.


Как это часто бывает: это зависит. Функции Arrow имеют другое поведение, чем декларации / выражения функций, поэтому давайте сначала рассмотрим различия:

1. Функции Lexical this и arguments

не имеют собственных привязок this или arguments. Вместо этого эти идентификаторы разрешаются в лексической области, как и любая другая переменная. Это означает, что внутри функции стрелки this и arguments относятся к значениям this и arguments в окружающей среде, функция стрелки определена в (т.е. «снаружи» стрелка )

В случае выражения функции, this относится к объекту, который был создан внутри createObject. В функциональном случае стрелки this относится к this самого createObject.

Это делает функции стрелок полезными, если вам нужно получить доступ к this текущей среды:

// currently common pattern
var that = this;
getData(function(data) {
  that.data = data;
});

// better alternative with arrow functions
getData(data => {
  this.data = data;
});

Обратите внимание, что это также означает, что не можно установить функцию стрелки this с .bind или .call.

Если вы не очень знакомы с this, рассмотрим чтение

2. Функции стрелок не могут быть вызваны с помощью new

ES2015 различает функции, доступные call , и функции, которые являются конструкцией . Если функция конструируется, ее можно вызвать с помощью new, то есть new User(). Если функция является вызываемой, ее можно вызвать без new (т. Е. Вызов нормальной функции).

Функции, созданные посредством деклараций / выражений функций, являются конструктивными и вызываемыми. Функции стрелок (и методы) являются только вызываемыми. class конструкторы только конструктивны.

Если вы пытаетесь вызвать функцию, не вызываемую вызовом, или построить неконструируемую функцию, вы получите ошибку времени выполнения.


Зная это, мы можем указать следующее.

Сменный:

  • Функции, которые не используют this или arguments.
  • Функции, которые используются с .bind(this)

Не сменный:

  • Функции конструктора
  • Функция / методы, добавленные к прототипу (поскольку они обычно используют функции this)
  • Variadic (если они используют arguments (см. ниже))

Давайте рассмотрим это более подробно с помощью ваших примеров:

Функция конструктора

Это не будет работать, потому что функции стрелок нельзя вызвать с помощью new. Продолжайте использовать объявление / выражение функции или используйте class.

Способы прототипа

Скорее всего нет, потому что методы прототипа обычно используют this для доступа к экземпляру. Если они не используют this, вы можете его заменить. Однако, если вы в первую очередь заботитесь о сжатом синтаксисе, используйте class с его синтаксисом сжатого метода:

class User {
  constructor(name) {
    this.name = name;
  }

  getName() {
    return this.name;
  }
}

Методы объекта

Аналогично для методов в объектном литерале. Если метод хочет ссылаться на сам объект через this, продолжайте использовать функциональные выражения или используйте новый синтаксис метода:

const obj = {
  getName() {
    // ...
  },
};

Обратные вызовы

Это зависит. Вы должны обязательно заменить его, если вы наложили внешний this или используете .bind(this):

// old
setTimeout(function() {
  // ...
}.bind(this), 500);

// new
setTimeout(() => {
  // ...
}, 500);

Но: Если код, вызывающий обратный вызов, явно устанавливает this на определенное значение , как это часто бывает с обработчиками событий, особенно с jQuery, и обратный вызов использует this (или arguments), вы не можете использовать функцию стрелки!

Variadic функции

Поскольку функции стрелок не имеют собственных arguments, вы не можете просто заменить их функцией стрелки. Однако ES2015 вводит альтернативу использованию arguments: параметр rest .

// old
function sum() {
  let args = [].slice.call(arguments);
  // ...
}

// new
const sum = (...args) => {
  // ...
};

Связанный вопрос:

Дополнительные ресурсы:

376
ответ дан Community 31 August 2018 в 11:48
поделиться

Посмотрите на этот пример Plnkr

Переменная this сильно отличается timesCalled с каждым нажатием кнопки увеличивается только на 1. Ответ на мой личный вопрос:

.click( () => { } )

и

.click(function() { })

создают одинаковое количество функции при использовании в цикле, как вы можете видеть из подсчета Guid в Plnkr.

2
ответ дан jmb.mage 31 August 2018 в 11:48
поделиться
Другие вопросы по тегам:

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