Рубрики
Без рубрики

Проблема ежедневного кодирования Java #005

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

Ежедневные проблемы с кодированием на Java (Серия из 8 частей)

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

Проблема

минусы(a, b) создает пару и автомобиль(пара) и cdr(пара) возвращает первый и последний элемент этой пары. Например, автомобиль(минусы(3, 4)) возвращает 3 , и cdr(минусы(3, 4)) возвращает 4 .

Учитывая эту реализацию минусов :

def cons(a, b):
   def pair(f):
       return f(a, b)
   return pair

Орудие автомобиль и cdr .

Стратегия

Спойлеры! Не смотрите ниже, если не хотите увидеть мое решение!

Ежедневная проблема кодирования в основном использует Python в своих подсказках (по крайней мере, из того, что я видел до сих пор), и поэтому первое, что нам нужно сделать, это перевести это с Python на Java. Фраза:

минусы(a, b) создает пару

…подразумевает, что cons(a, b) создает объект, возвращая экземпляр этого объекта, давайте назовем этот класс объектов Пара . car() и cdr() , затем являются методами, которые принимают одну пару в качестве аргумента и возвращают первое или второе значение в паре соответственно.

Python может разрешить a и b быть любого неопределенного типа, но в Java мы должны использовать дженерики или явно указывать a и б какой-то класс. Давайте посмотрим, сможем ли мы сделать это с помощью дженериков.

Наконец, внутри cons(a, b) происходит немного странная структура. В нем мы определяем функцию более высокого порядка пара , который принимает в качестве аргумента вторую функцию, f , которую мы применяем к a и b (в таком порядке) , не зная, что f опережает время. Обратите внимание, что минусы(a, b) не возвращает пару(f) с любым конкретным аргументом, переданным как f — он возвращает пара , сама функция. Эффект заключается в том, что cons(a, b) фактически возвращает частично определенную функцию, f(a, b) , где a и b известны, но f нет.

Если вы запустите это в оболочке Python, вы получите:

>>> def cons(a, b):
...     def pair(f):
...         return f(a,b)
...     return pair
... 
>>> cons(1, 2)

Обратите внимание, как cons() возвращает функцию (с именем пара ). В Python мы можем передать определенную функцию или лямбду этому возвращаемому значению:

>>> ret = cons(2, 3)
>>> ret(lambda a, b: a*b)
6
>>> ret(lambda a, b: a+b)
5
>>> def fn(a, b):
...     return a**b
... 
>>> ret(fn)
8

Такое поведение будет немного сложнее реализовать в Java.

Код

Во-первых, давайте посмотрим, сможем ли мы определить эту константу функция в Java. минусы – это метод, который принимает два общих аргумента и возвращает … что-то:

public ??? cons (T a, U b) {
 ...
}

В минусах у нас определена другая функция, пара , который принимает функцию в качестве аргумента (в частности, Бифункцию и возвращает эту функцию , примененную к аргументам , предоставленным внешней функции cons :

public pair (BiFunction f) {
  return f(a,b); // a and b defined in cons()
}

Однако с этим есть некоторые проблемы. Во-первых, вы не можете определить функцию внутри такой функции в Java . Вместо этого мы должны определить эту внутреннюю функцию в терминах функциональных интерфейсов. pair() – это функция, которая принимает другую функцию f в качестве аргумента и возвращает некоторое значение. Это делает его обычной старой функцией :

Function, R> pair = f -> f.apply(a, b);

Сигнатура типа для внутренней функции потому что мы ничего не знаем о функции f . Все, что мы знаем, это то, что он принимает два аргумента, которые могут быть одного или разных типов ( T и U ), и он возвращает значение, которое может быть одним из этих типов или совершенно другим третьим типом ( R ). Но когда мы определяем f и передаем его в пару , f вернет значение типа R , который затем становится возвращаемым значением (и типом) пары . Таким образом, возвращаемый тип пары также должен быть R .

Включение этого в минусы дает:

jshell> public class DCP005 {
   ...>   public Function,R> cons(T a, U b) {
   ...>     Function,R> pair = f -> f.apply(a,b);
   ...>     return pair;
   ...>   }
   ...> }

…где возвращаемый тип cons – это просто тип пары , так как это то, что возвращает cons .

Обратите внимание, что это находится в оболочке . Мы можем явно (и очень подробно) объявить все типы, такие как:

jshell> Function,Integer> exa = (new DCP005()).cons(2, 3)
exa ==> DCP005$$Lambda$24/85777802@4d41cee

…или, если мы с радостью проигнорируем предупреждения, просто:

jshell> Function exb = (new DCP005()).cons(2, 3)
|  Warning:
|  unchecked call to cons(T,U) as a member of the raw type DCP005
|  Function exb = (new DCP005()).cons(2, 3);
|                 ^-----------------------^
exb ==> DCP005$$Lambda$24/85777802@2833cc44

Чтобы применить этот метод, мы можем сделать:

jshell> exa.apply((x,y) -> x+y)
$28 ==> 5

jshell> exa.apply((x,y) -> x*y)
$29 ==> 6

…это немного сложнее с exb , но это работает:

jshell> exb.apply((BiFunction)((x,y) -> (Integer)x+(Integer)y))
|  Warning:
|  unchecked call to apply(T) as a member of the raw type java.util.function.Function
|  exb.apply((BiFunction)((x,y) -> (Integer)x+(Integer)y))
|  ^-----------------------------------------------------^
$3 ==> 5

jshell> exb.apply((BiFunction)((x,y) -> (Integer)x*(Integer)y))
|  Warning:
|  unchecked call to apply(T) as a member of the raw type java.util.function.Function
|  exb.apply((BiFunction)((x,y) -> (Integer)x*(Integer)y))
|  ^-----------------------------------------------------^
$4 ==> 6

Поэтому нам нужны объявления классов где-то , будь то при создании функции с cons() или когда мы вызываем его с помощью apply() . Теперь единственное, что все еще немного неудобно, – это то, как нам пришлось вызывать cons в качестве члена объекта DCP005 . Мы можем устранить это, объявив и класс, и метод как статические , , но мы не можем сделать это в оболочке

package DCP;

import java.util.function.BiFunction;
import java.util.function.Function;

public class App {

  public static  Function,R> cons(T a, U b) {
    Function,R> pair = f -> f.apply(a,b);
    return pair;
  }

}

Компиляция этого и загрузка пакета в оболочку делает это намного приятнее и проще:

$ mvn package
...

$ jshell --class-path target/005-1.0-SNAPSHOT.jar
...

jshell> import static DCP.App.*

jshell> cons(2, 3).apply((x,y) -> x+y)
$4 ==> 5

jshell> cons(2, 3).apply((x,y) -> x*y)
$5 ==> 6

Теперь у нас почти тот же синтаксис, что и в Python! (Я бы также сказал, что синтаксис более интуитивно понятен .) При определении и использовании таким образом Java будет определять типы объектов, передаваемых в:

jshell> cons(2, "3").apply((x,y) -> x)
$9 ==> 2

jshell> cons(2, "3").apply((x,y) -> x + y)
$10 ==> "23"

Когда с трудной частью покончено, давайте займемся легкой частью: фактическим решением подсказки. Для этого нам просто нужны две функции, которые принимают возвращаемое значение из cons() и возвращают либо левый, либо правый член пары. Что-то вроде:

  public static  T car(Function,T> cons) {
    return cons.apply((a,b) -> a);
  }

  public static  U cdr(Function,U> cons) {
    return cons.apply((a,b) -> b);
  }

Обобщения здесь сложны, но то, что car говорит, в основном, о том, что функция, предоставляемая ей, должна принимать два объекта типа T и U , и возвращают объект типа T . cdr говорит, что предоставленная ему функция должна принимать два объекта типа T и U , и возвращают объект типа U .

Определенно более подробный для программиста, но более безопасный для типов. И детализация для пользователя (в оболочке ) точно равна версии Python (при условии, что мы уже определили car и cdr также в Python):

>>> car(cons(2, "3"))
2

>>> cdr(cons(2, "3"))
'3'
jshell> car(cons(2, "3"))
$2 ==> 2

jshell> cdr(cons(2, "3"))
$3 ==> "3"

Единственная разница в том, что нам нужно импортируйте пакет, который мы определили в Java.

Обсуждение

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

Весь код для решения моих ежедневных проблем с кодированием доступен по адресу github.com/awwsmm/daily .

Предложения? Дайте мне знать в комментариях.

Ежедневные проблемы с кодированием на Java (Серия из 8 частей)

Оригинал: “https://dev.to/awwsmm/java-daily-coding-problem-005-1phg”