一个更好的Java Kata功能解决方案



我正在努力学习更多关于函数编程的知识。我做了一些训练视频,我想我会做一个卡塔。

也许我只是选了一个不好的,我试图制作谓词来做过滤器,但它似乎添加了比需要的更多的代码。我相信还有更好的方法。

谢谢!

卡塔-------

n:         2
oldValue: 'a'
newValue: 'o'
"Vader said: No, I am your father!" -> "Vader soid: No, I am your fother!"
1     2          3        4       -> 2nd and 4th occurence are replaced

package kata;

import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiPredicate;

public class ReplaceEveryNth {
public static Integer interPos = 0;

//works but isn't great. How do you make this functional?
public static String replaceNth(String text, Integer n, Character oldValue, Character newValue){
//String builder allows for the setCharAt Function to swap char
StringBuilder sb = new StringBuilder(text);

//make an array of all the positions the char is found at.
ArrayList<Integer> foundAt = new ArrayList<>();
sb.toString().chars().forEach( c -> {
interPos++;
if (c == oldValue) foundAt.add(interPos);
});

//need to for mod div
AtomicInteger index = new AtomicInteger();
index.set(1);

//if mod this pos is 0, then swap it.
foundAt.forEach(pos -> {
System.out.println("pos Mods: " + pos + " " + index);
if (index.get() % n == 0) {
sb.setCharAt(pos-1, newValue);
}
index.getAndIncrement();
});

return sb.toString();
}

}

编辑这个更新的方法,它真的不起作用吗?但它使用了一个单独的循环。

public static String replaceNth(String text, Integer n, Character oldValue, Character newValue){
char[] chars = text.toCharArray();
char[] ret = new char[chars.length];
int counter = 1;
for (int i = 0; i < chars.length; ++i)
{
ret[i] = chars[i];
if (chars[i] == oldValue) {
if (counter % n == 0) {
ret[i] = newValue;
counter = 0;
}
counter++;
}
}
return new String(ret);
}

不确定这里是否可以使用正则表达式方法,但通常可以创建一个参数化模式,用newValue:替换oldValue的第N次出现

"(oldValue)([^oldValue]*(oldValue[^oldValue]*){occurrence - 2})(oldValueToReplace)"
此处使用3组:

  1. (oldValue)-首次出现
  2. ([^oldValue]*(oldValue[^oldValue]*){occurrence - 2})-零个或多个非oldValue条目,后面跟着带有后缀的oldValue,应出现occurrence - 2
  3. (oldValueToReplace)—第N个要替换的事件

此外,字符类[]之外的值应该转义。

示例实现:

public static String replaceNth(String text, int n, char oldValue, char newValue) {
String pattern = String.format("(%2$s)([^%1$s]*(%2$s[^%1$s]*){%3$d})(%2$s)", oldValue, Pattern.quote(String.valueOf(oldValue)), n - 2);

return text.replaceAll(pattern, "$1$2" + newValue);
}    

测试:

System.out.println(replaceNth("Vader said: No, I am your father!", 2, 'a', 'o'));
System.out.println(replaceNth("... .... ..... .... ... ", 3, '.', 'x'));
System.out.println(replaceNth("xxxx abcd x dbca xxxx", 5, 'x', 'O'));
System.out.println(replaceNth("+---------------------", 7, '-', '+'));

输出:

Vader soid: No, I am your fother!
..x ..x. .x..x ..x. .x. 
xxxx abcd O dbca xxxx
+------+------+------+

Dave,我已经组装了一些我认为更具功能性的东西(我不是功能性编程专家,我的Java有点生疏(。我认为我对";玩得开心";而不是更直接地回答你的问题,所以我对此感到抱歉。

根据我所读到的,函数编程的关键之一是识别";纯函数";(没有副作用的(并在此基础上再接再厉。几乎所有有用的代码都会有不纯净的函数,但通过将尽可能多的逻辑转移到纯函数中,有助于将不纯净的东西集中在希望包含良好的区域中。

我所做的是将问题分解为几个不同的通用函数,并用它们来解决手头的问题。我没有马上找到这个解决方案。我开始分解,随着工作的进行,我调整了功能,使其越来越通用。我花了好几次迭代才得到下面的内容。早期的迭代有点难看,但当我完成这些事情时,我感到惊喜的是,我能够把它发展到现在的地步。

谢谢你提出这个问题。我在回答这个问题时学到了很多。我希望反过来我也能帮助你。

// Splits a string by the given character into a list of sub-strings
public static List<String> splitBy(String text, Character splitValue) {
return Arrays.stream(text.split(Pattern.quote(splitValue.toString()), -1)).toList();
}
// Generates an infinite sequence where every Nth item is one value
// and all other values are another
// generateEveryNthSequence(3, 'A', 'B') =? ['B', 'B', 'A', 'B', 'B', 'A', ...]
public static <T> Stream<T> generateEveryNthSequence(int n, T everyNthValue, T everyOtherValue) {
return Stream.iterate(1, i -> i + 1).map(i -> i % n == 0 ? everyNthValue : everyOtherValue);
}
// Combines two sequences by alternating the values
// ['A','B','C'] and ['1','2','3'] => ['A', '1', 'B, '2', 'C', '3']
public static <T> Stream<T> alternateValues(Stream<T> stream1, Stream<T> stream2) {
Iterator<T> iterator1 = stream1.iterator();
Iterator<T> iterator2 = stream2.iterator();
return Stream.iterate(iterator1, t -> t == iterator1 ? iterator2 : iterator1)
.takeWhile(t -> t.hasNext())
.map(t -> t.next());
}
public static String replaceNth(String text, Integer n, Character oldValue, Character newValue){
// "V", "der s", "id: No, I ", "m your f", "ther!"
List<String> segments = splitBy(text, oldValue);
// "a", "o", "a", "o", ...
Stream<String> separators = generateEveryNthSequence(n, newValue.toString(), oldValue.toString());
// "V", "a", "der s", "o", "id: No, I ", "a", "m your f", "o", "ther!", "a"
Stream<String> alternatingItems = alternateValues(segments.stream(), separators);
// "V", "a", "der s", "o", "id: No, I ", "a", "m your f", "o", "ther!"
Stream<String> alternatingItemsTrimmed = alternatingItems.limit(segments.size() * 2 - 1);
// "Vader soid: No, I am your fother!"
return alternatingItemsTrimmed.collect(Collectors.joining());
}

我想介绍一个替代解决方案

public class App {
public static void main(String[] args) {
String input = "Vader said: No, I am your father!";
String result = replacenth(input, 'a', 'o', 2);
System.out.println(input);
System.out.println(result);
System.out.println(result.equalsIgnoreCase("Vader soid: No, I am your fother!"));
}
private static String replacenth(String input, char search, char replace, int n) {
return IntStream.range(1, input.length() + 1)
.mapToObj(i -> input.substring(0, i))
.map(s -> shouldReplace(s, search, n)
? replace : s.charAt(s.length() - 1))
.collect(Collector.of(
StringBuilder::new,
StringBuilder::append,
StringBuilder::append,
StringBuilder::toString));
}
private static boolean isEqual(String s, char c) {
return s.charAt(s.length() -1) == c;
}
private static Long countOccurences(String s, char c){ 
return s.chars().filter(x -> x == c).count();
}
private static boolean shouldReplace(String s, char search, int n) {
return isEqual(s, search) && countOccurences(s, search) % n == 0;
}

}

最新更新