在代码中表示2x2的逻辑电网高效 [英] Expressing 2x2 Logic Grid in Code Efficiently

查看:166
本文介绍了在代码中表示2x2的逻辑电网高效的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在的事件处理程序,我响应一个值的变化。我有机会获得旧值和新的价值,并希望根据不同的变化是一定要做的事情。



每个不同的结局会做的动作/功能的某种组合X,Y或Zž接受执行这些并不重要的-1和1之间的秩序的一个参数。



看下面的逻辑网格。旧值是标签的最左边的列,新值标签的最上面一行:

 新:
0,= 0
-------- -------
老:0 |没有Y,Z(1)
= 0!| X,Z(-1),X,Y - Z(0)是好的,但不是必需为这个象限

什么是代表呢?



我在C#中工作,但会接受任何语言的答案,因为它不是一个真正的语言问题,我的好方法。可以翻译任何



例如:

 如果(OLDVALUE == 0安培;&安培; NEWVALUE == 0)回报; 
如果(OLDVALUE!= 0)X();
如果(!NEWVALUE = 0),Y();
Z(OLDVALUE = 0 -1:0 + NEWVALUE = 0 1:!?!?0);



我想这看起来不错,但也有其他方面,它可以这样做。



  INT其中=(OLDVALUE == 0?0:1)+(NEWVALUE == 0?0:2)

开关(所){
案例1:
X(); Z(-1);
中断;
案例2:
Y(); Z(1);
中断;
案例3:
X(); Y();
中断;
}

这实际上是一个比我处理稍微简单情况。在于OLDVALUE和newValue非零且彼此相等的情况下,对待NEWVALUE,如果它是0



随意回答,因为给予或与此附加约束。还是有一点点,但我认为这是太多,开始了。如果事情是有意思以后,我就不是在这里,还是在一个新的问题出现的其余部分。



我想我问这个问题,因为我结束了这些逻辑常格的事情,他们并不总是2x2的,有时他们有点大。这是很好的注意,我可以处理整个条纹一些回应像注意到,X为完成每次OLDVALUE时间!= 0,但似乎我开始运行到该乞求一些表现力的逻辑来处理模式它更普遍,而不是费力地把它变成如果当时else语句。我的意思是,这将是非常酷的,如果我能提供一种逻辑的电网,让编译器选择最佳的方式来处理它。



只是在做一些野生头脑风暴:

 条件:
OLDVALUE == 0? 0:1
NEWVALUE == 0? 0:2

操作:
X = {假,真,假,真}
Y = {假的,假的,真正的,真实}
Z( - 1)= true其中条件= 1
Z(1)= true其中条件= 2

你有什么想法?我将奖励任何物质参与的。


解决方案

让我们看看你的问题,从另一个角度来看。当设计一个代码的身体,我尝试应用以下原则:




  1. 请它正确

  2. 请说清楚。

  3. 请它简洁。

  4. 请它快。
    ......的顺序。



所有这些,给一个程度或另一种,是主观的。但是,合理的人容易找到共同点 - 有是关于它们的对立面往往更一致。但是,抛开......



这里的首要任务是确保您的代码将正常工作。显然,有多种实现,实现这一点 - 但我也想补充一点的它,它很容易证明,实施正确很重要的。实现这一目标的一个方法是使代码看起来更像规范(更多关于这个版本)。



第二个重点是要确保在今后,当开发人员(包括原作者!)看着这段代码,他们将能够理解它在做什么的时候了。更复杂的(阅读:看中的)的实施,它是一个开发人员立刻明白是什么代码正在做更难



< STRONG>第三优先级 - 短,简洁的代码,是一个局部站在反对前两个,以使代码更简洁,可能会导致你使用比是解决实际需要更复杂的构造一个愿望。问题。虽然它保持代码的短是很重要的,我们不应该使它难以理解的密集做到这一点。



最后的优先项目 - 性能 - 只有当它的事项事项这样,我的意思是,你不应该执行但从性能的角度,除非你进行分析,并确定它是在你的系统瓶颈复杂化。



所以,现在我们已经看了原则应该推动我们的决定,让我们把它们应用到手头的问题。您所提供的代码是怎样一个非常明确的规范应该表现。让我们试着坚持他们:

 无效YourMethod(INT属性oldValue,INT newValue)以
{
布尔oldValueNonZero =属性oldValue!= 0;
布尔newValueNonZero =为newValue!= 0;

如果(oldValueNonZero){X(); }
如果(newValueNonZero){Y(); }
如果(oldValueNonZero&安培;&安培; newValueNonZero){Z(); }
}



那么,为什么我喜欢这个特定的实现。让我们来分析一下。



第一,请注意,我选择了创建临时布尔捕捉测试结果对于不论是非零旧/新值。通过捕捉这些值,我避免执行计算不止一次,我也使代码更易读(见下文)。



通过选择描述性的名称 oldValueNonZero newValueNonZero 我正在落实清楚地表明我的期望。这既提高了代码的可读性,并清楚表达我的意图,未来的开发谁拥有读它。



第三,注意身体的如果()试验被包裹在 {} 支架 - 这有助于减少的机会,未来的实施更改将中断的行为 - 由于意外,包括一个新的情况下,例如。使用单行 IFS 是未来的问题的良方。



最后后,我不要尝试短路比较早退出功能。如果性能是极为重要的,早早退出可能是有用的。但除此之外,它可以更容易理解的方法的行为,如果只有一个出口点(1)



是否这段代码做了规范说什么?的我想是的。



是否容易阅读和理解。<​​/ em>的至少我眼睛,我会说是的。



这段代码的短路逻辑最紧凑的或复杂的方式?的几乎肯定不会.. 。但它的其他品质超过弥补的是,在我看来。



你是否喜欢代码的这种特殊结构或不就是,在一定程度上的问题品味和风格。但我希望的原理我已经奠定了我是如何选择组织它可以帮助你在将来这样的决定。



您已经有时表明您碰到类似的逻辑网格的问题,但那里的人的案件数量都多。这类问题可以为两个单独的原因变得复杂:



<醇>
  • ,该参数可以取增加值的数目 - 他们可以采取上。一般形式的M×N

  • 的维数增加 - 换言之,有更多的变量,在规则中包括: MxNxOxP ... XZ



  • 一个广义解决问题的方法(如另一响应指示),是在该溶液作为多维矩阵编码 - 并限定一组用于每一种情况下的操作。然而,这是完全有可能的规则重叠 - 而且它可能需要相当的崩溃一起案件,为简便起见。



    我来处理一般情况下的反应是......它依赖。如果条件可以降低到一些非常小的多的情况下,比势在必行如果/ else逻辑可能仍然是解决问题的最好方法。如果条件的数量非常大,比它可能是有意义的使用声明的方式,在你使用某种类型的查找表或矩阵编码的情况下。



    1> - 一个常见的​​例外仅具有一种方法单一出口点是前提条件的原则。它的清洁,首先检查所有/任何先决条件,而失败(退出)的方法,如果他们受到侵犯,避免嵌套和反向逻辑。


    In an event handler I'm responding to the change of a value. I have access to the old value and the new value and want to do certain things depending on what the change is.

    Each different outcome will do some combination of actions/functions X, Y, or Z. Z accepts a parameter between -1 and 1. Order of performing these is not important.

    Look at the following logic grid. The old value is the leftmost column of labels, and the new value is the top row of labels:

              New:
              0          !=0
              --------   -------
    Old:  0 | nothing    Y, Z(1)
        !=0 | X, Z(-1)   X, Y    -- Z(0) is okay but not required for this quadrant
    

    What would be a good way to represent this?

    I'm working in C# but will accept answers in any language since it's not really a language question—I can translate whatever.

    Example:

    if (oldvalue == 0 && newvalue == 0) return;
    if (oldvalue != 0) X();
    if (newvalue != 0) Y();
    Z(oldvalue != 0 ? -1 : 0 + newvalue != 0 ? 1 : 0);
    

    I suppose that looks pretty good, but there are other ways it could be done.

    int which = (oldvalue == 0 ? 0 : 1) + (newvalue == 0 ? 0 : 2)
    
    switch (which) {
       case 1:
          X(); Z(-1);
          break;
       case 2:
          Y(); Z(1);
          break;
       case 3:
          X(); Y();
          break;
    }
    

    This is actually a slightly simpler case than what I'm dealing with. In the case that oldvalue and newvalue are nonzero and equal to each other, treat newvalue as if it was 0.

    Feel free to answer as given or with this additional constraint. There's still a little bit more but I think it's too much to start off with. If things seem interesting afterward, I'll present the rest either here or in a new question.

    I guess I'm asking the question because I end up with these logic grid things often, and they aren't always 2x2, sometimes they're a bit bigger. It's nice to notice that I can handle some responses with entire "stripes" like noticing that X is done every time the oldvalue != 0, but it seems like I'm starting to run into a pattern that begs for some expressive logic to handle it more generally instead of laboriously turning it into if then else statements. I mean, it would be really cool if I could provide a sort of grid of logic and let the compiler figure out the best way to handle it.

    Just doing some wild brainstorming:

    Conditions:
    oldvalue == 0 ? 0 : 1
    newvalue == 0 ? 0 : 2
    
    Actions:
    X = {false, true, false, true}
    Y = {false, false, true, true}
    Z(-1) = true where condition = 1
    Z(1) = true where condition = 2
    

    What are your ideas? I'll reward any material involvement at all.

    解决方案

    Let's look at your problem from another point of view. When designing a body of code, I try to apply the following principle:

    1. Make it correct.
    2. Make it clear.
    3. Make it concise.
    4. Make it fast. ... in that order.

    All of these, to one extent or another, are subjective. However, reasonable people tend to find common ground - and there's often more agreement about their opposites. But that aside...

    The first priority here is making sure that your code will work correctly. Clearly, there are multiple implementation that achieve this - but I would also add that it's important that it be easy to demonstrate that the implementation is correct. One way of achieving this is to make the code read more like the spec (more on this later).

    The second priority is to make sure that in the future, when a developer (including the original author!) looks at this code they'll be able to understand what it's doing right away. The more sophisticated (read: fancy) the implementation, the harder it is for a developer to immediately understand what the code is doing.

    The third priority - short, concise code, is one that partially stands in opposition to the first two. A desire to make the code more concise, may lead you to use more complex constructs than are actually necessary to solve the problem. While it's important to keep the code short, we shouldn't do this by making it unintelligibly dense.

    The last priority - performance - only matters when it matters. By this, I mean that you shouldn't complicate the implementation from the point of view of performance unless you've performed profiling and identified it as bottleneck in your system.

    So now that we've looked at the principles that should drive our decisions, let's apply them to the problem at hand. You've provided a very clear specification of how the code is supposed to behave. Let's try to adhere to them:

    void YourMethod( int oldValue, int newValue )
    {
        bool oldValueNonZero = oldValue != 0;
        bool newValueNonZero = newValue != 0;
    
        if( oldValueNonZero ) { X(); }
        if( newValueNonZero ) { Y(); }
        if( oldValueNonZero && newValueNonZero ) { Z(); }
    }
    

    So why do I like this particular implementation. Let's break it down.

    First, note that I've chosen to create temporary boolean to capture the result of testing the old/new value for whether they are nonzero. By capturing these values, I avoid performing the calculation more than once, and I also make the code more readable (see below).

    Second, by choosing the descriptive names oldValueNonZero and newValueNonZero I'm making the implementation clearly indicate my expectations. This both improves the readability of the code and clearly conveys my intent to future developers who have to read it.

    Third, note that the body of the if() test is wrapped in { and } brackets - this helps reduce that chance that future changes to the implementation will break the behavior - by accidentally including a new case, for instance. Using single-line ifs is a recipe for future problems.

    Finally, I don't try to short-circuit the comparison and exit the function early. If performance were extremely important, an early exit may be useful. But otherwise, it makes it easier to understand the behavior of a method if there's only one exit point(1).

    Does this code do what the spec says? I believe so.

    Is it easy to read and understand. At least to my eyes, I would say yes.

    Is this code the most compact or sophisticated way of short-circuiting the logic? Almost certainly not ... but it's other qualities more than make up for that, in my opinion.

    Whether you like this particular structure of code or not is, to some extent, a matter of taste and style. But I hope that the principles I've laid out about how I chose to organize it may help you make such decisions in the future.

    You've indicated that you sometimes run into similar "logic-grid" problems, but ones where the number of cases are more numerous. These kinds of problems can become complicated for two separate reasons:

    1. The number of values that the parameters can take on increase - they can take on the general form MxN.
    2. The number of dimensions increase - in other words, there are more variables to include in the rules: MxNxOxP...xZ.

    One generalized solution to the problem (as another response indicates), is to encode the solution as multidimensional matrix - and define a set of actions for each case. However, it's entirely possible for the rules to overlap - and it's probably desirable to collapse equivalent cases together, for simplicity.

    My response to dealing with the general case is ... that it depends. If the conditions can be reduced to some very small number of cases, than imperative if/else logic may still be the best way to solve the problem. If the number of conditions are very large, than it may make sense to use a declarative approach, in which you use some kind of lookup table or matrix to encode the cases.

    1> - One common exception to the principle of only having a single exit point from a method is for preconditions. It's cleaner to avoid nesting and inverted logic by first checking all/any preconditions, and failing (exiting) the method if they are violated.

    这篇关于在代码中表示2x2的逻辑电网高效的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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