过滤真值表 [英] Filtering with truth tables

查看:181
本文介绍了过滤真值表的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

想象一个具有布尔标志的Person类,表示该人是否可以使用 - 默认情况下设置为false。

Imagine a Person class with a boolean flag indicating whether or not the person is employable - set to false by default.

public class Person{
    boolean employable = false;
    ...
}

现在想象一下 > external 作用于Person对象的布尔方法。例如,在实用程序类中考虑静态布尔方法。

Now imagine having some external boolean methods which act on Person objects. For example, consider static boolean methods in a utility class.

public class PersonUtil{
    public static boolean ofWorkingAge(Person p){
        if(p.getAge() > 16) return true;
        return false;
    }
    ...
}

布尔静态方法是本质上类似于布尔值函数,即谓词

Boolean static methods are in essence analogous to boolean valued functions i.e. predicates.

我们可以从谓词中构造一个2 ^(#predicates)-by-#谓词真值表。例如,给出三个谓词: ofWorkingAge 我们可以构建以下8-by-3真值表:

We can construct a 2^(#predicates)-by-#predicates truth table out of predicates. For example, given three predicates: ofWorkingAge, ofGoodCharacter, isQualified we can construct the following 8-by-3 truth table:

T T T
T T F
T F T
T F F
F T T
F T F
F F T
F F F

我们现在想雇用具有理想品质的人。让 + 表示我们希望考虑有人可以使用(即将其雇用能力标志设置为真实)和 - 相反。

We now want to employ people with desirable qualities. Let + indicate that we wish to consider somebody employable (i.e. set their employability flag to true) and - the opposite.

T T T | +
T T F | +
T F T | +
T F F | -
F T T | +
F T F | -
F F T | -
F F F | -

现在设想有一个Person对象的集合。对于每个人,我们根据三个谓词调整其就业能力标志。我们还更新了一个计数(这迫使我们使用整个真实表而不是正面的),所以给1,000个人,我们想要得到如下结果:

Now imagine having a collection of Person objects. For each person we adjust their employability flag according to the three predicates. We also update a count (this forces us to use the entire truth table instead of just the positives), so that given 1,000 people we want to end up with something like:

T T T | +   100
T T F | +   200
T F T | +   50
T F F | -   450
F T T | +   50
F T F | -   50
F F T | -   50
F F F | -   50

大概这可以被认为是使用真值表进行过滤。设置就业能力标志和更新计数是一个相当引人入胜的例子,但您可以轻松地看到我们可能会如何设置和更新更复杂的事情。

Presumably this can be thought of as filtering with truth tables. Setting employability flags and updating counts is a rather contrived example but you can easily see how we might instead want to set and update much more complicated things.

QUESTION

有没有办法优雅的做?我可以想到两个解决方案:

Is there a way of elegantly doing this? I can think of two solutions:

Clunky解决方案

编码if,else if,else链。

Have a giant hand coded if, else if, else chain.

if(ofWorkingAge && ofGoodCharacter && isQualified){
    c1++;
    p.setEmployable(true)
}
else if(ofWorkingAge && ofGoodCharacter && !isQualified){
    c2++;
    p.setEmployable(true)
}
...
else if(!ofWorkingAge && !ofGoodCharacter && isQualified){
    c7++;
}
else{
    c8++;
}

这只是坏的。

稍微更智能的解决方案

传递谓词(可能在数组中)和方法集合的句子。让该方法生成相应的真值表。循环人,设定就业能力,并返回一系列计数。

Pass predicates (perhaps in an array) and a collection of sentences to a method. Let the method generate the corresponding truth table. Loop over the people, set their employability, and return an array of counts.

我可以看到功能界面如何完成。 此SO答案具有潜在的相关性。您可以将 PrintCommand 更改为 IsQualified ,并将 callCommand 转换为Person而不是字符串。但是,这也似乎是笨拙的,因为我们然后必须为我们想出的每个谓词都有一个新的界面文件。

I can see how things could be done with functional interfaces. This SO answer is potentially relevant. You could change PrintCommand to IsQualified and pass callCommand a Person instead of a string. But this also seems kindah clunky because we'd then have to have a new interface file for every predicate we come up with.

有没有其他的Java 8-ish这样做?

Is there any other Java 8-ish way of doing this?

推荐答案

我们先从您的谓词列表开始:

Let's start with the list of predicates you have:

List<Predicate<Person>> predicates = Arrays.<Predicate<Person>> asList(
        PersonUtil::ofWorkingAge, PersonUtil::ofGoodCharacter,
        PersonUtil::isQualified);



< NamedPredicate class:

public static class NamedPredicate<T> implements Predicate<T> {
    final Predicate<T> predicate;
    final String name;

    public NamedPredicate(Predicate<T> predicate, String name) {
        this.predicate = predicate;
        this.name = name;
    }

    @Override
    public String toString() {
        return name;
    }

    @Override
    public boolean test(T t) {
        return predicate.test(t);
    }
}

(可以附加 BitSet 或类似这样的效率,但 String 名称也不错)。

(one may attach BitSet or something like this for efficiency, but String names are also fine).

现在我们需要生成一个真值表,它是名为codeTTF的谓词的新列表,并且能够应用给定的源谓词组合,否定或否定。这可以通过一些功能性编程魔法轻松生成:

Now we need to generate a truth table which is a new list of predicates having names like "T T F" and able to apply the given combination of source predicates, negated or not. This can be generated easily with a bit of functional programming magic:

Supplier<Stream<NamedPredicate<Person>>> truthTable
    = predicates.stream() // start with plain predicates
        .<Supplier<Stream<NamedPredicate<Person>>>>map(
            // generate a supplier which creates a stream of 
            // true-predicate and false-predicate
            p -> () -> Stream.of(
                    new NamedPredicate<>(p, "T"),
                    new NamedPredicate<>(p.negate(), "F")))
        .reduce(
            // reduce each pair of suppliers to the single supplier
            // which produces a Cartesian product stream
            (s1, s2) -> () -> s1.get().flatMap(np1 -> s2.get()
                            .map(np2 -> new NamedPredicate<>(np1.and(np2), np1+" "+np2))))
        // no input predicates? Fine, produce empty stream then
        .orElse(Stream::empty);

as truthTable 是一个供应商< Stream> ,您可以根据需要重复使用它多次。还要注意的是,所有的 NamedPredicate 对象是按需生成的,我们不会将它们存储在任何地方。我们尝试使用这个供应商:

as truthTable is a Supplier<Stream>, you can reuse it as many times as you want. Also note that all the NamedPredicate objects are generated on the fly by demand, we don't store them anywhere. Let's try to use this supplier:

truthTable.get().forEach(System.out::println);

输出是:

T T T
T T F
T F T
T F F
F T T
F T F
F F T
F F F

现在您可以通过真值表对个人集合进行分类,例如,以下列方式:

Now you can classify the persons collection by the truth table, for example, in the following way:

Map<String,List<Person>> map = truthTable.get().collect(
    Collectors.toMap(np -> np.toString(), // Key is string like "T T F"
        // Value is the list of persons for which given combination is true
        np -> persons.stream().filter(np).collect(Collectors.toList()),
        // Merge function: actually should never happen; 
        // you may throw assertion error here instead
        (a, b) -> a,
        // Use LinkedHashMap to preserve an order
        LinkedHashMap::new));

现在,您可以轻松获得计数:

Now you can easily get the counts:

map.forEach((k, v) -> System.out.println(k+" | "+v.size()));

要更新可用的字段,我们需要知道如何指定所需的真值表。让它成为这样的真实字符串的集合:

To update the employable field we need to know how the desired truth table is specified. Let it be the collection of truth strings like this:

Collection<String> desired = Arrays.asList("T T T", "T T F", "T F T", "F T T");

在这种情况下,您可以使用以前生成的地图:

In this case you may use the previously generated map:

desired.stream()
       .flatMap(k -> map.get(k).stream())
       .forEach(person -> person.setEmployable(true));

这篇关于过滤真值表的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆