Ежедневные проблемы с кодированием на 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 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 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”