【问题标题】:How do I convert this program into java 8 functional style using streams?如何使用流将此程序转换为 java 8 功能样式?
【发布时间】:2017-03-30 00:35:19
【问题描述】:

问题
我编写了一个程序来找出给定字符串中字符的大写和小写的所有可能性。

一个例子是, 输入——“ab”/“Ab”等——其中任何一个 输出 - ["ab","Ab","aB","AB"]

代码

算法不正确 - 请检查以下内容。

public static ArrayList<String> permuteUCLC(String a)
{

    String s=new String(a.toLowerCase());
    ArrayList<String> arr = new ArrayList<>();
    arr.add(a);
    int l = a.length();
    for(int i=0;i<=l;i++)
    {
        for(int j=i+1;j<=l;j++)
        {
            arr.add(s.substring(0,i)+s.substring(i,j).toUpperCase()+s.substring(j,l));
        }
    }
    Collections.sort(arr);
    Collections.reverse(arr);
    return arr;
}

注意
在提出我的算法错误的问题后,我意识到。我会在适当的时候尝试上传正确的算法。


子序列代码(正确代码) 这是查找所有子序列并将它们大写的代码。假设所有字符都是唯一的。如何找到指数 并以功能的方式实现它?

public static void permuteSubsequence(String a)
{
    int n=a.length();
    for(int i=0;i<(1<<n);i++)
    {
        String buff="";
        for(int j=0;j<n;j++)
        {
            if(((1<<j)&i)!=0) 
            {
                buff=buff+new Character(a.charAt(j)).toString().toUpperCase();
            }
            else
            {
                buff = buff + a.charAt(j);
            }
        }
        System.out.println(buff);
    }
}

从上述案例中提取索引。即 1 的索引并将它们大写。


请求

如何使用 Java 流将上述代码转换为函数式风格?

我面临的问题是在 map 方法中模拟索引范围。 另外,有没有办法生成字符串的 Stream 来将相同的字符串复制到所有元素中,类似于IntStream.range(a,b)

public static List<String> permuteStreams(String a)
{
    int l=(int)(Math.pow(2,a.length())-1)
    ArrayList<String> al = new ArrayList<>();
    for(int i=0;i<=l;i++)
        al.add(a);//Generate a stream and copy back into arraylist maybe if possible?

    List<String> sl = al.stream()
      .map()//incomplete code
      .collect(Collectors.toList());
    return sl;
}

【问题讨论】:

  • "我编写了一个程序来找出给定字符串的大写和小写的所有排列。"太可怕了,我希望这是一个学术练习。
  • 您能详细说明一下吗?声明是不必要的还是问题不清楚?
  • 回答你的小问题 - 如果我需要一个包含 X 个不可变对象副本的列表,我会使用 Arrays.fill 来填充我想要的大小的数组,然后使用 Arrays.asList 来获取列出来。
  • new Character(a.charAt(j)).toString().toUpperCase() 这样的表达真的很可怕。你为什么不寻找一个直接的解决方案,即首先Character.toUpperCase(a.charAt(j))
  • @Sbodd:如果您需要一个包含 X 个对象副本的列表,只需使用 Collections.nCopies(X, object),无需先填充数组……如果您需要 X 重复 @987654332 的流@,你可以直接在 nCopies 列表中调用 stream(),它的作用与 IntStream.range(0, X).mapToObj(ignored -&gt; object) 相同……

标签: java algorithm functional-programming java-8 java-stream


【解决方案1】:

不要通过拆分和连接字符串来置换字符串,这是一项非常昂贵的操作,完全没有必要。考虑到“大写”和“小写”恰好是两种状态,两种状态项的置换组合应该敲响警钟,我们正在谈论binary numbers。计算机中的整数是位的组合,具有两种状态,遍历这些位的所有可能排列就像遍历整数范围一样容易。

即范围0, 1, 2, 3, 4, 5, 6, 7 的二进制数表示为000, 001, 010, 011, 100, 101, 110, 111。现在想象0 代表“小写”,1 代表“大写”,代表三个字符串,您就差不多完成了。

所以剩下的任务就是根据相关位是否设置,将String的字符转换为大写或小写。有几种方法可以实现这一点。以下代码创建一个最初为小写的字符串作为所有迭代的起点,并将字符切换为大写,如果设置了位:

public static void permuteSubsequence(String s) {
    if(s.isEmpty()) {
        System.out.println();
        return;
    }
    String lower = s.toLowerCase(), upper = s.toUpperCase();
    if(s.length()!=lower.length() || s.length()!=upper.length())
        throw new UnsupportedOperationException("non trivial case mapping");
    LongStream.range(0, 1L<<Math.min(lower.length(), 62))
        .mapToObj(l -> {
            StringBuilder sb=new StringBuilder(lower);
            BitSet.valueOf(new long[] { l }).stream()
                  .forEach(ix -> sb.setCharAt(ix, upper.charAt(ix)));
            return sb.toString();
        })
        .forEach(System.out::println);
}

请注意,此实现仅通过置换较长字符串的第一个 62 字符来作弊,因为用于迭代的签名 long 不允许更多,但置换 62 字符已经允许 4611686018427387904 组合,所以即使我们假设打印一个变体只需要一纳秒,我们也需要一百多年才能将它们全部打印出来。所以你永远不会注意到作弊。

字符串的大写/小写转换不必产生相同长度的字符串。此实现将拒绝具有非平凡大小写映射的字符串,这种排列是不可能的。

有一点需要改进,就是省略大小写形式不同的字符。这可以通过首先识别可置换字符(它们的位置)并仅置换这些字符来完成:

public static void permuteSubsequence(String s) {
    int[] permutable = IntStream.range(0, s.length())
        .filter(i->Character.toLowerCase(s.charAt(i))!=Character.toUpperCase(s.charAt(i)))
        .toArray();
    if(permutable.length == 0) {
        System.out.println(s);
        return;
    }
    String lower = s.toLowerCase(), upper = s.toUpperCase();
    if(s.length()!=lower.length() || s.length()!=upper.length())
        throw new UnsupportedOperationException("non trivial case mapping");
    LongStream.range(0, 1L<<Math.min(permutable.length, 62))
        .mapToObj(l -> {
            StringBuilder sb=new StringBuilder(lower);
            BitSet.valueOf(new long[] { l }).stream()
                  .map(bit -> permutable[bit])
                  .forEach(ix -> sb.setCharAt(ix, upper.charAt(ix)));
            return sb.toString();
        })
        .forEach(System.out::println);
}

这样,permuteSubsequence("Mr.X"); 将打印出来

mr.x
Mr.x
mR.x
MR.x
mr.X
Mr.X
mR.X
MR.X

【讨论】:

  • 请注意,如果您检测到非平凡的大小写映射,您可以尝试使用Normalizer 将其转换为可以进行这种排列的形式。在某些情况下,这会起作用。仅考虑单个 chars 的任何其他解决方案在这些情况下都不起作用。
【解决方案2】:

我面临的问题是在 map 方法中模拟索引范围。

使用IntStream.range(或rangeClosed)从索引更改问题 - start。请参阅this answer 了解更多信息。这是原始代码的(主要)面向流的版本:

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.IntStream;
import java.util.stream.Stream;

import static java.util.function.Function.identity;
import static java.util.stream.Collectors.toList;

public static List<String> permuteUCLCStream(String a)
{
    String s = a.toLowerCase();
    int l = a.length();
    Stream<String> result = IntStream
            .rangeClosed(0, l)
            .mapToObj(i -> IntStream
                    .rangeClosed(i + 1, l)
                    .mapToObj(j -> s.substring(0, i) + s.substring(i, j).toUpperCase() + s.substring(j, l)))
            .flatMap(identity());
    List<String> arr = Stream.concat(Stream.of(a), result).sorted().collect(toList());
    Collections.reverse(arr);
    return arr;
}

【讨论】:

  • 谢谢。一个绝妙的方法。但是,我改变了不仅考虑连续子串而且考虑子序列的问题。另外,如果你能解释 rangeClosed()?直到,我找到了如此准确的答案,这就行了。
【解决方案3】:

为此需要考虑一些事项。首先,流 api 的构建假设流中的位置与您对每个元素执行的任何操作都不相关。为了自然地适应这一点,您需要重新设计算法以不关心索引号。

其次,您没有将任何单个字母映射到任何特定结果(或结果集),因此使用输入字符串的字符流并不是特别有用。你应该寻找其他东西来使用流。

您为固定的非流方法提出的算法实际上非常适合流式传输 - 将 buff 的内容粘贴到流中并通过流 api 对其进行所有更改。我认为这应该可以解决问题:

public static List<String> permuteStreams(String a) {
    Stream<StringBuilder> partialSolutions = Stream.of(new StringBuilder(a.length()));

    for (char c : a.toCharArray()) {
        partialSolutions = partialSolutions.flatMap(solution -> Stream.of(
                new StringBuilder(solution).append(Character.toLowerCase(c)),
                solution.append(Character.toUpperCase(c))));
    }

    return partialSolutions.map(StringBuilder::toString).collect(Collectors.toList());
}

或者,如果您不介意将字符串复制操作加倍:

public static List<String> permuteStreams(String a) {
    Stream<String> partialSolutions = Stream.of("");

    for (char c : a.toCharArray()) {
        partialSolutions = partialSolutions.flatMap(solution -> Stream.of(
                solution + Character.toLowerCase(c),
                solution + Character.toUpperCase(c));
    }

    return partialSolutions.collect(Collectors.toList());
}

这样实际上可能会节省一些空间,因为 StringBuilder 会分配额外的容量以防您不打算进行添加。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-05-30
    • 2014-05-29
    • 1970-01-01
    • 2021-09-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多