繁体   English   中英

一个更好的 Java Kata 函数式解决方案

[英]A better Functional solutions to this Java Kata

我正在尝试更多地了解函数式编程。 我做了一些训练视频,我想我会做一个 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替换每个第 N 次出现的oldValue

"(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;
}

}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM