使用' case'有多种条件

时间:2017-11-28 18:50:54

标签: ruby switch-statement

我试图编写一个案例陈述,其中包含两个条件,如下所示:

class Pokemon:

    def __init__(self, kind, level):
        self.level = level
        self.kind = kind  # The kind is just a component now.

    def evolve(self):
        # Create a new instance of the next evolution stage 
        # and use it to replace the previous kind.
        self.kind = self.kind.evolution_stage()


class Bulbasaur:

    def __init__(self):
        self.base_health = 45
        # A reference to the next evolution stage class.
        self.evolution_stage = Ivysaur


class Ivysaur:

    def __init__(self):
        self.base_health = 60
        # self.evolution_stage = Venusaur


# Pass a Bulbasaur instance as the `kind` argument.
pokemons = [Pokemon(Bulbasaur(), 3)]
print(pokemons[0].kind, pokemons[0].kind.base_health)
pokemons[0].evolve()  # Changes the .kind attribute.
print(pokemons[0].kind, pokemons[0].kind.base_health)

我很乐意让这个工作。我更愿意理解为什么我的例子失败了。

已编辑添加:

感谢所有反馈!灵感,我意识到结合两个案例变量允许我将它们折叠成一个简单的switch语句的单个值......

roll1 = rand(1..6)
roll2 = rand(1..2)

result = case[roll1, roll2]
  when [1..3 && 1]
    "Low / Low"
  when [4..6 && 1]
    "High / Low"
  when [1..3 && 2]
    "Low / High"
  when [4..6 && 2]
    "JACKPOT!!"
end

puts result

虽然这解决了我眼前的问题,但它并没有提升我的基础知识 - 与我收到的所有令人敬畏的反馈相比,这是一个微不足道的洞察力。真诚的谢谢!

4 个答案:

答案 0 :(得分:4)

您的代码有两个问题。首先,这个:

[1..3 && 1]

是一个包含一个元素的数组。由于..的优先级低于&&,因此您实际上正在撰写1..(3 && 1),这只是一种复杂的1..1方式。这意味着您的case确实是:

case[roll1, roll2]
  when [1..1]
    "Low / Low"
  when [4..1]
    "High / Low"
  when [1..2]
    "Low / High"
  when [4..2]
    "JACKPOT!!"
end

第二个问题是Array没有覆盖===使用的case运算符,因此您将使用Object#===,它只是{{1}的别名1}}。这意味着您的Object#==相当于:

case

if([roll1, roll2] == [1..1]) "Low / Low" elsif([roll1, roll2] == [4..1]) "High / Low" elsif([roll1, roll2] == [1..2]) "Low / High" elsif([roll1, roll2] == [4..2]) "JACKPOT!!" end 永远不会等于[roll1, roll2]因为[some_range]逐个元素进行比较而Array#==永远不会roll1一个范围;此外,您还要比较不同大小的数组。

这意味着你有一个复杂的说法:

==

我可能只是使用result = nil

if

或者您可以明确使用result = if (1..3).include?(roll1) && roll2 == 1 'Low / Low' elsif (4..6).include?(roll1) && roll2 == 1 'High / Low' ...

===

但请注意result = if (1..3) === roll1 && roll2 == 1 'Low / Low' elsif (4..6) === roll1 && roll2 == 1 'High / Low' ... 的优先级低。

答案 1 :(得分:2)

案例陈述,表达和案例平等

when语句并不像你想象的那样真正起作用,也不是数组文字。在其基本形式中,import geotrellis.raster._ import geotrellis.spark._ import geotrellis.spark.tiling._ import geotrellis.vector._ import org.apache.spark.HashPartitioner import org.apache.spark.rdd.RDD import java.util.UUID // see https://stackoverflow.com/questions/47359243/geotrellis-get-the-points-that-fall-in-a-polygon-grid-rdd val pointValueRdd : RDD[Feature[Point,Double]] = ??? val squareGridRdd : RDD[Polygon] = ??? // Since we'll be grouping by key and then joining, it's work defining a partitioner // up front. val partitioner = new HashPartitioner(100) // First, we'll determine the bounds of the Polygon grid // and the average height and width, to create a GridExtent val (extent, totalHeight, totalWidth, totalCount) = squareGridRdd .map { poly => val e = poly.envelope (e, e.height, e.width, 1) } .reduce { case ((extent1, height1, width1, count1), (extent2, height2, width2, count2)) => (extent1.combine(extent2), height1 + height2, width1 + width2, count1 + count2) } val gridExtent = GridExtent(extent, totalHeight / totalCount, totalWidth / totalCount) // We then use this for to construct a LayoutDefinition, that represents "tiles" // that are 1x1. val layoutDefinition = LayoutDefinition(gridExtent, tileCols = 1, tileRows = 1) // Now that we have a layout, we can cut the polygons up per SpatialKey, as well as // assign points to a SpatialKey, using ClipToGrid // In order to keep track of which polygons go where, we'll assign a UUID to each // polygon, so that they can be reconstructed. If we were dealing with PolygonFeatures, // we could store the feature data as well. If those features already had IDs, we could // also just use those IDs instead of UUIDs. // We'll also store the original polygon, as they are not too big and it makes // the reconstruction process (which might be prone to floating point errors) a little // easier. For more complex polygons this might not be the most performant strategy. // We then group by key to produce a set of polygons that intersect each key. val cutPolygons: RDD[(SpatialKey, Iterable[Feature[Geometry, (Polygon, UUID)]])] = squareGridRdd .map { poly => Feature(poly, (poly, UUID.randomUUID)) } .clipToGrid(layoutDefinition) .groupByKey(partitioner) // While we could also use clipToGrid for the points, we can // simply use the mapTransform on the layout to determien what SpatialKey each // point should be assigned to. // We also group this by key to produce the set of points that intersect the key. val pointsToKeys: RDD[(SpatialKey, Iterable[PointFeature[Double]])] = pointValueRdd .map { pointFeature => (layoutDefinition.mapTransform.pointToKey(pointFeature.geom), pointFeature) } .groupByKey(partitioner) // Now we can join those two RDDs and perform our point in polygon tests. // Use a left outer join so that polygons with no points can be recorded. // Once we have the point information, we key the RDD by the UUID and // reduce the results. val result: RDD[Feature[Polygon, Double]] = cutPolygons .leftOuterJoin(pointsToKeys) .flatMap { case (_, (polyFeatures, pointsOption)) => pointsOption match { case Some(points) => for( Feature(geom, (poly, uuid)) <- polyFeatures; Feature(point, value) <- points if geom.intersects(point) ) yield { (uuid, Feature(poly, (value, 1))) } case None => for(Feature(geom, (poly, uuid)) <- polyFeatures) yield { (uuid, Feature(poly, (0.0, 0))) } } } .reduceByKey { case (Feature(poly1, (accum1, count1)), Feature(poly2, (accum2, count2))) => Feature(poly1, (accum1 + accum2, count1 + count2)) } .map { case (_, feature) => // We no longer need the UUID; also compute the mean feature.mapData { case (acc, c) => acc / c } } 使用threequals运算符(case)将顶级表达式与when语句中的表达式进行比较。

在Ruby中,几乎所有东西都是表达式。在您的示例中,您尝试将值数组与解析为单元素数组的数组进行匹配。考虑:

===

基本上,您的顶级数组永远不会匹配您提供的任何数组表达式作为条件。你想要的可能是这样的:

[1..3 && 2]
#=> [1..2]

[1..3 && 2].count
#=> 1

[1..3 && 2].map &:class
#=> [Range]

在这种类型的构造中,您没有使用具有顶级值的三元组表达式来进行比较。相反,你是从一对表达式构造一个truthy或falsey布尔值。使用额外的when语句将其充实,然后在仍然无法进行无效比较时调试表达式。

答案 2 :(得分:0)

除了“JACKPOT!!”之外,你有两个不同的问题,通过单独处理它们更容易处理。

def result_of_rolls(roll1, roll2)
  if (4..6).cover?(roll1) && roll2 == 2
    "JACKPOT!!"
  else
    "%s / %s" % [(1..3).cover?(roll1) ? "Low" : "High",
                 roll2 == 1 ? "Low" : "High"]
  end
end

result_of_rolls(2,1) #=> "Low / Low"
result_of_rolls(4,1) #=> "High / Low"
result_of_rolls(3,2) #=> "Low / High"
result_of_rolls(5,2) #=> "JACKPOT!!"

如果有三个或更多个卷,而不是两个卷,可以看出这种方法比检查roll1roll2的所有值组合的方法更有效, roll3,等等。

答案 3 :(得分:0)

如果您的示例不仅仅是针对一般情况的人为MCVE,而是您的实际问题,或者如果您的实际问题非常相似,那么还有一些其他想法:

视为两个独立的问题,结合,处理特例:

result = "#{roll1 < 4 ? 'Low' : 'High'} / #{roll2 < 2 ? 'Low' : 'High'}".
           sub('High / High', 'JACKPOT!!')

同样的想法写得不同:

result = [roll1 < 4, roll2 < 2].
           map { |low| low ? 'Low' : 'High' }.
           join(' / ').
           sub('High / High', 'JACKPOT!!')

又一次,但有点傻:

result = [roll1 < 4, roll2 < 2].
           join(' / ').
           gsub('true', 'Low').gsub('false', 'True').
           sub('High / High', 'JACKPOT!!')

使用布尔值,因为他们只需要进行==比较:

result = case [roll1 > 3, roll2 > 1]
         when [false, false]
           "Low / Low"
         when [true, false]
           "High / Low"
         when [false, true]
           "Low / High"
         when [true, true]
           "JACKPOT!!"
         end

case [(4..6) === roll1, roll2 == 2]也会有效。请注意我indented differently。)

将卷组合成一个数字:

result = case roll1 * (-1)**roll2
         when -3..-1
           "Low / Low"
         when -6..-4
           "High / Low"
         when 1..3
           "Low / High"
         when 4..6
           "JACKPOT!!"
         end

同样的想法写得不同:

result = case roll2 * 10 + roll1
         when 11..13
           "Low / Low"
         when 14..16
           "High / Low"
         when 21..23
           "Low / High"
         when 24..26
           "JACKPOT!!"
         end

同样,只是不同的代码风格:

result = case roll1 * (-1)**roll2
         when -3..-1 then "Low / Low"
         when -6..-4 then "High / Low"
         when  1..3  then "Low / High"
         when  4..6  then "JACKPOT!!"
         end