Ежедневные проблемы с кодированием на 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 (BiFunctionf) { 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 staticFunction ,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 staticT 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”