Ссылка на лямбда при ее инициализации [дубликат]

Нет, не разбит, но большинство десятичных дробей должно быть аппроксимировано

Резюме

Арифметика с плавающей точкой точный, к сожалению, он не очень хорошо сочетается с нашим обычным представлением числа base-10, так что получается, что мы часто даем ему ввод, который немного от того, что мы написали.

Даже простые числа, такие как 0.01, 0.02, 0.03, 0.04 ... 0.24, не представляются точно как двоичные дроби, даже если в мантиссе были тысячи бит точности, даже если у вас были миллионы. Если вы отсчитываете с шагом 0,01, пока вы не достигнете 0,25, вы получите первую фракцию (в этой последовательности), представленную в base10 и base2. Но если вы попытались использовать FP, ваш 0,01 был бы слегка отключен, поэтому единственный способ добавить 25 из них до хорошего точного 0.25 потребовал бы длинной цепи причинности, включающей защитные биты и округление.

Мы постоянно даем аппарату FP что-то вроде простого в базе 10, но это повторяющаяся фракция в базе 2.

Как это произошло?

Когда мы пишем в десятичной форме, каждая дробь является рациональным числом форма

& nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; x / (2n + 5n).

В двоичном выражении мы получаем только член 2n , то есть:

& nbsp ; & NBSP; & NBSP; & NBSP; & NBSP; & NBSP; & NBSP; & NBSP; & NBSP; & NBSP; x / 2n

Итак, в десятичной форме мы не можем представлять 1/3. Поскольку база 10 включает в себя 2 как простой коэффициент, каждое число, которое мы можем записать как двоичную дробь , также может быть записано в виде базовой дроби. Однако вряд ли что-либо, что мы пишем как base10, представляется в двоичном виде. В диапазоне от 0,01, 0,02, 0,03 ... 0,99 только цифры три могут быть представлены в нашем формате FP: 0,25, 0,50 и 0,75, поскольку они равны 1/4, 1/2, и 3/4 - все числа с простым множителем, использующим только 2n-член.

В базе 10 мы не можем представлять 1/3. Но в двоичном коде мы не можем делать 1/10 или 1/3.

Так что, хотя каждая двоичная дробь может быть записана в десятичной системе, обратное неверно. И фактически большинство десятичных дробей повторяются в двоичном формате.

Работа с ним

Разработчикам обычно дают указание & Lt; epsilon , лучшим советом может быть округление до целочисленных значений (в библиотеке C: round () и roundf (), т. е. оставаться в формате FP), а затем сравнивать. Округление до определенной длины десятичной дроби решает большинство проблем с выходом.

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

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

Мне нравится ответ на пиццу от Chris , потому что он описывает фактическую проблему, а не только обычная ручная работа о «неточности». Если бы FP были просто «неточными», мы могли бы исправить , что и сделали бы это несколько десятилетий назад. Причина, по которой у нас нет, - это то, что формат FP компактен и быстр, и это лучший способ хрустить множество чисел. Кроме того, это наследие космической эры и гонки вооружений и ранние попытки решить большие проблемы с очень медленными компьютерами с использованием небольших систем памяти. (Иногда отдельные магнитные сердечники для 1-битного хранилища, но это другая история. )

Заключение

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

43
задан user2418306 3 March 2016 в 10:55
поделиться

19 ответов

Пошел на этот вопрос во время лекции по Лямбдасу, которая использовала Фибоначчи в качестве возможного варианта использования.

Вы можете сделать рекурсивную лямбду следующим образом:

import java.util.function.Function;

public class Fib {

   static Function<Integer, Integer> fib;

   public static void main(String[] args) {
       fib = (n) -> { return n > 1 ? fib.apply(n-1) + fib.apply(n-2) : n; };

       for(int i = 0; i < 10; i++){
           System.out.println("fib(" + i + ") = " + fib.apply(i));
       }
   }
}

Что вы нужно иметь в виду?

  • Lambdas оцениваются при выполнении -> они могут быть рекурсивными
  • Использование лямбда-переменной внутри другой лямбды требует инициализации переменной - > перед определением рекурсивной лямбда вы должны определить ее с помощью foo-value
  • , используя локальную лямбда-переменную внутри lambda, чтобы переменная была окончательной, поэтому ее нельзя переопределить -> использовать класс / объект переменная для лямбда, поскольку она инициализируется значением по умолчанию
1
ответ дан 000000000000000000000 25 August 2018 в 23:43
поделиться

Я обычно использую (один раз для всех функциональных интерфейсов) общий вспомогательный класс, который обертывает переменную типа функционального интерфейса. Этот подход решает проблему с локальной инициализацией переменных и позволяет коду выглядеть более четко.

В случае возникновения этого вопроса код будет выглядеть следующим образом:

// Recursive.java
// @param <I> - Functional Interface Type
public class Recursive<I> {
    public I func;
}

// Test.java
public double factorial(int n) {

    Recursive<IntToDoubleFunction> recursive = new Recursive<>();
    recursive.func = x -> (x == 0) ? 1 : x * recursive.func.applyAsDouble(x - 1);

    return recursive.func.applyAsDouble(n);
}
34
ответ дан Andrey Morozov 25 August 2018 в 23:43
поделиться

Вы можете определить рекурсивную лямбду в качестве переменной экземпляра или класса:

static DoubleUnaryOperator factorial = x -> x == 0 ? 1
                                          : x * factorial.applyAsDouble(x - 1);

, например:

class Test {
    static DoubleUnaryOperator factorial = x -> x == 0 ? 1
                                             : x * factorial.applyAsDouble(x - 1));
    public static void main(String[] args) {
        System.out.println(factorial.applyAsDouble(5));
    }
}

печатает 120.0.

2
ответ дан assylias 25 August 2018 в 23:43
поделиться

Я слышал в JAX в этом году, что «lambads не поддерживают рекурсию». Под этим утверждением подразумевается, что «это» внутри лямбда всегда относится к окружающему классу.

Но мне удалось определить - по крайней мере, как я понимаю термин «рекурсия» - рекурсивная лямбда и он выглядит следующим образом:

interface FacInterface {
  int fac(int i);
}
public class Recursion {
  static FacInterface f;
  public static void main(String[] args)
  {
    int j = (args.length == 1) ? new Integer(args[0]) : 10;
    f = (i) -> { if ( i == 1) return 1;
      else return i*f.fac( i-1 ); };
    System.out.println( j+ "! = " + f.fac(j));
  }
}

Сохраните это внутри файла «Recursion.java» и с двумя командами «javac Recursion.java» и «java Recursion» это сработало для меня.

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

1
ответ дан beck 25 August 2018 в 23:43
поделиться
public class Main {
    static class Wrapper {
        Function<Integer, Integer> f;
    }

    public static void main(String[] args) {
        final Wrapper w = new Wrapper();
        w.f = x -> x == 0 ? 1 : x * w.f.apply(x - 1);
        System.out.println(w.f.apply(10));
    }
}
2
ответ дан Danil Gaponov 25 August 2018 в 23:43
поделиться

Еще один рекурсивный факториал с Java 8

public static int factorial(int i) {
    final UnaryOperator<Integer> func = x -> x == 0 ? 1 : x * factorial(x - 1);
    return func.apply(i);
}
0
ответ дан Dmytro Chopenko 25 August 2018 в 23:43
поделиться

Если вам нужно часто делать такие вещи, другой способ - создать вспомогательный интерфейс и метод:

public static interface Recursable<T, U> {
    U apply(T t, Recursable<T, U> r);
}

public static <T, U> Function<T, U> recurse(Recursable<T, U> f) {
    return t -> f.apply(t, f);
}

И затем напишите:

Function<Integer, Double> fact = recurse(
    (i, f) -> 0 == i ? 1 : i * f.apply(i - 1, f));

(В то время как я делал это в общих чертах с ссылочными типами, вы также можете создавать примитивные версии).

Это заимствует из старого трюка в Little Lisper для создания неназванных функций.

Я не уверен, что когда-либо сделаю это в производственном коде, но это интересно ...

3
ответ дан Ian Robertson 25 August 2018 в 23:43
поделиться

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

public static IntToLongFunction reduce(int zeroCase, LongBinaryOperator reduce) {
  return new Object() {
    IntToLongFunction f = x -> x == 0
                               ? zeroCase
                               : reduce.applyAsLong(x, this.f.applyAsLong(x - 1));
  }.f;
}

public static void main(String[] args) {
  IntToLongFunction fact = reduce(1, (a, b) -> a * b);
  IntToLongFunction sum = reduce(0, (a, b) -> a + b);
  System.out.println(fact.applyAsLong(5)); // 120
  System.out.println(sum.applyAsLong(5)); // 15
}
0
ответ дан JbGi 25 August 2018 в 23:43
поделиться

Следующие действия работают, но это кажется тайным.

import java.util.function.Function;

class recursion{

Function<Integer,Integer>  factorial_lambda;  // The positions of the lambda declaration and initialization must be as is.

public static void main(String[] args) {  new recursion();}

public recursion() {
 factorial_lambda=(i)->{
        if(i==1)
            return 1;
        else
            return i*(factorial_lambda.apply(i-1));
    };
    System.out.println(factorial_lambda.apply(5));
 }
}

// Output 120
2
ответ дан Jerrolds 25 August 2018 в 23:43
поделиться

У меня нет компилятора Java8, поэтому я не могу проверить свой ответ. Но будет ли это работать, если вы определили переменную «факт» как окончательную?

final IntToDoubleFunction fact = x -> {
    return  ( x == 0)?1:x* fact.applyAsDouble(x-1);
};
-1
ответ дан JonasCz 25 August 2018 в 23:43
поделиться

Вы можете создать рекурсивную функцию с помощью этого класса:

public class Recursive<I> {
    private Recursive() {

    }
    private I i;
    public static <I> I of(Function<RecursiveSupplier<I>, I> f) {
        Recursive<I> rec = new Recursive<>();
        RecursiveSupplier<I> sup = new RecursiveSupplier<>();
        rec.i = f.apply(sup);
        sup.i = rec.i;
        return rec.i;
    }
    public static class RecursiveSupplier<I> {
        private I i;
        public I call() {
            return i;
        }
    }
}

И тогда вы можете использовать любой функциональный интерфейс всего в 1 строке, используя лямбда и определение вашего функционального интерфейса, как показано ниже:

Function<Integer, Integer> factorial = Recursive.of(recursive ->
        x -> x == 0 ? 1 : x * recursive.call().apply(x - 1));
System.out.println(factorial.apply(5));

Я нашел это очень интуитивно понятным и простым в использовании.

0
ответ дан Jose Da Silva 25 August 2018 в 23:43
поделиться
public class LambdaExperiments {

  @FunctionalInterface
  public interface RFunction<T, R> extends Function<T, R> {
    R recursiveCall(Function<? super T, ? extends R> func, T in);

    default R apply(T in) {
      return recursiveCall(this, in);
    }
  }

  @FunctionalInterface
  public interface RConsumer<T> extends Consumer<T> {
    void recursiveCall(Consumer<? super T> func, T in);

    default void accept(T in) {
      recursiveCall(this, in);
    }
  }

  @FunctionalInterface
  public interface RBiConsumer<T, U> extends BiConsumer<T, U> {
    void recursiveCall(BiConsumer<T, U> func, T t, U u);

    default void accept(T t, U u) {
      recursiveCall(this, t, u);
    }
  }

  public static void main(String[] args) {
    RFunction<Integer, Integer> fibo = (f, x) -> x > 1 ? f.apply(x - 1) + f.apply(x - 2) : x;

    RConsumer<Integer> decreasingPrint = (f, x) -> {
      System.out.println(x);
      if (x > 0) f.accept(x - 1);
    };

    System.out.println("Fibonnaci(15):" + fibo.apply(15));

    decreasingPrint.accept(5);
  }
}

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

0
ответ дан leonel dossantos 25 August 2018 в 23:43
поделиться

Локальные и анонимные классы, а также lambdas, фиксируют локальные переменные по значению при их создании. Поэтому им невозможно ссылаться на себя, захватывая локальную переменную, потому что значение для указания на себя не существует еще в момент их создания.

Код в локальных и анонимных классах может все еще ссылайтесь на себя, используя this. Однако this в лямбда не относится к лямбда; он ссылается на this из внешней области.

Вместо этого вы можете захватить изменчивую структуру данных, например массив:

IntToDoubleFunction[] foo = { null };
foo[0] = x -> { return  ( x == 0)?1:x* foo[0].applyAsDouble(x-1);};

, хотя вряд ли это изящное решение.

12
ответ дан newacct 25 August 2018 в 23:43
поделиться

Один из способов - записать вторичную функцию, helper, которая принимает в качестве аргументов функцию и число, а затем записывает нужную функцию fact = helper(helper,x).

Так же:

BiFunction<BiFunction, Double, Double> factHelper =
        (f, x) -> (x == 0) ? 1.0 : x*(double)f.apply(f,x-1);
Function<Double, Double> fact =
        x -> factHelper.apply(factHelper, x);

Мне кажется, что это немного более элегантно, чем полагаться на семантику углового случая, такую ​​как замыкание, которое фиксирует ссылку на изменяемую структуру или позволяет саморегуляцию с предупреждением о возможности «могущества не будет инициализировано ».

Тем не менее, это не идеальное решение из-за системы типов Java - генераторы не могут гарантировать, что f, аргумент factHelper, имеет тот же тип, что и factHelper (т. е. те же типы ввода и типы вывода), поскольку это будет бесконечно вложенным общим.

Таким образом, вместо этого более безопасным решением может быть:

Function<Double, Double> fact = x -> {
    BiFunction<BiFunction, Double, Double> factHelper =
        (f, d) -> (d == 0) ? 1.0 : d*(double)f.apply(f,d-1);
    return factHelper.apply(factHelper, x);
};

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

15
ответ дан rationalis 25 August 2018 в 23:43
поделиться

Учитывая тот факт, что «this» в лямбда относится к содержащему классу, следующие компиляции без ошибок (с добавленными зависимостями, конечно):

public class MyClass {
    Function<Map, CustomStruct> sourceToStruct = source -> {
        CustomStruct result;
        Object value;

        for (String key : source.keySet()) {
            value = source.get(key);

            if (value instanceof Map) {
                value = this.sourceToStruct.apply((Map) value);
            }

            result.setValue(key, value);
        }

        return result;
    };
}
1
ответ дан Rebel Geek 25 August 2018 в 23:43
поделиться

Немного похоже на самый первый ответ ...

public static Function<Integer,Double> factorial;

static {
    factorial = n -> {
        assert n >= 0;
        return (n == 0) ? 1.0 : n * factorial.apply(n - 1);
    };
}
2
ответ дан Rene.v.P. 25 August 2018 в 23:43
поделиться

Проблема заключается в том, что лямбда-функции хотят работать с переменными final, тогда как нам нужна изменчивая Function -референция, которая может быть заменена нашей лямбдой.

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

Я изменил свой пример, чтобы использовать IntUnaryOperator вместо IntToDoubleFunction, так как мы просто работаем над Integers в любом случае здесь.

import org.junit.Test;
import java.util.function.IntUnaryOperator;
import static org.junit.Assert.assertEquals;

public class RecursiveTest {
    private IntUnaryOperator operator;

    @Test
    public void factorialOfFive(){
        IntUnaryOperator factorial = factorial();
        assertEquals(factorial.applyAsInt(5), 120); // passes
    }

    public IntUnaryOperator factorial() {
        return operator = x -> (x == 0) ? 1 : x * operator.applyAsInt(x - 1);
    }
}
0
ответ дан tomaj 25 August 2018 в 23:43
поделиться

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

Function<Integer,Double> facts = x -> { return  ( x == 0)?1:x* facts.apply(x-1);};
BiFunction<Integer,Double,Double> factAcc= (x,acc) -> { return (x == 0)?acc:factAcc.apply(x- 1,acc*x);};
Function<Integer,Double> fact = x -> factAcc.apply(x,1.0) ;

public static void main(String[] args) {
   Test test = new Test();
   test.doIt();
}

 public void doIt(){
int val=70;
System.out.println("fact(" + val + ")=" + fact.apply(val));
}
}
2
ответ дан user2678835 25 August 2018 в 23:43
поделиться

Вы также можете определить его как локальную переменную, создав последний массив размера один (например, Function []), а затем назначьте функцию элементу 0. Сообщите мне, если вам нужен точный синтаксис

1
ответ дан Victor Grazi 25 August 2018 в 23:43
поделиться
Другие вопросы по тегам:

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