1

writing a simple monte carlo simulation of a neutron beam. Having trouble with the geometry logic (whether something is in one environment or another). My issue is that Ruby seems to be processing the conditions sequentially and keeping the first value it comes to.

The code below illustrates this quite nicely:

def checkPosition(*args)

  polyCylRad = 2.5
  polyCylFr = 15
  polyCylB = -2.0
  borPolyBoxL = 9.0 / 2
  pbCylRad = 3.0
  pbBoxL = 10.0 / 2
  cdBoxL = 9.5 / 2

  position = Array.new
  material = String.new

  args.each do |item|
    position << item.inspect.to_f
  end
  xSquared = position.at(0) ** 2
  ySquared = position.at(1) ** 2
  zSquared = position.at(2) ** 2
  modX = Math.sqrt(xSquared)
  modY = Math.sqrt(ySquared)
  modZ = Math.sqrt(zSquared)

  puts xSquared
  puts Math.sqrt(ySquared + zSquared) <= polyCylRad
  puts (position.at(0) >= polyCylB)
  puts (position.at(0) <= polyCylFr)
  puts (position.at(0) >= polyCylB)and(position.at(0) <= polyCylFr)
  puts (position.at(0) <= polyCylFr)and(position.at(0) >= polyCylB)

  puts zSquared


  polyCylinder = (Math.sqrt(ySquared + zSquared) <= polyCylRad)and((position.at(0) >= polyCylB)and(position.at(0) <= polyCylFr) )
  puts polyCylinder
  borPolyBox = ((modX <= borPolyBoxL)or(modY < borPolyBoxL)or(modZ <= borPolyBoxL)) and not((modX >= cdBoxL)or(modY >= cdBoxL)or(modZ >= cdBoxL)) and not(Math.sqrt(ySquared + zSquared) <= polyCylRad)
  puts borPolyBox
  cadmiumShield = ((modX <= cdBoxL)or(modY < cdBoxL)or(modZ <= cdBoxL)) and not((modX >= pbBoxL)or(modY >= pbBoxL)or(modZ >= pbBoxL)) and not(Math.sqrt(ySquared + zSquared) <= polyCylRad)
  puts cadmiumShield
  leadShield = ( ((modX <= pbBoxL)or(modY <= pbBoxL)or(modZ <= pbBoxL)) or ((position.at(0) <= ployCylFr)and(Math.sqrt(ySquared + zSquared) <= pbCylRad)) ) and not(Math.sqrt(ySquared + zSquared) <= polyCylRad)
  puts leadShield

  if (polyCylinder) : material = "poly"
  elsif(borPolyBox) : material = "borPoly"
  elsif(cadmiumSheild) : material = "cd"
  elsif(leadSheild) : material = "pb"
  elsif(material == nil) : position = Array.new
  end

  thisEnvironment = Array.new
  thisEnvironment << position << material
  puts thisEnvironment.at(0)
  puts thisEnvironment.at(1)
end

checkPosition(40, 0, 0)

call the code whatever you want, but give it *args as an argument (I am lazy and may want to add more args in the future) then call it with 3 floats, wrt the geometry set up in the logic and you'll see what I mean.

My question is: how do I get it to work like it should (ie evaluating the logic correctly) without a whole bunch of nested if's? (which is what I am about to remake, however it is a nightmare to read and memory is cheap.)

pad
  • 41,040
  • 7
  • 92
  • 166
morb
  • 11
  • 2
  • 1
    Can you be more specific about how you want it to evaluate the logic? What should the code output? – wdebeaum Nov 30 '10 at 21:09
  • Well, at the moment all I want to look at are the truth values given to the various domains. This is dependent upon the argument array. My problem is outlined in the first bunch of puts statements, the order of the evaluated logic matters, when it should not *regardless of whether I use &&, || or or and and*, why is this? I don't think it is this, but: I am using the jEdit interpreter (which I am sure just looks in my PATH). – morb Nov 30 '10 at 23:03

4 Answers4

3

You've typed "Sheild" a few times where you probably meant "Shield"

In the context you're using them, you should be using && instead of and, || instead of or, and ! instead of not. The reason is that or and and have such a low precedence that they will cause your assignment operators to not work the way you want. For example,

a = b and c

evaluates as

(a = b) and c

Such that a is always assigned the value b, and then in the result is truthy, c is evaluated (and discarded). On the other hand,

a = b && c

evaluates as

a = (b && c)

Which is what you want in this code.

Beyond that, I would move all of this code into a class, so that I can create lots of little methods for things:

class PositionChecker

  def initialize(*args)
    @x, @y, @z = *args
  end

  def checkPosition
    ...
  end

end

Look for opportunities to replace local variables in checkPosition with method calls. For example, you could move borPolyBox into its own method (once all of the values it uses are methods of their own):

class PositionChecker
  ...
  def borPolyBox
    ((modX <= borPolyBoxL)||(modY < borPolyBoxL)||(modZ <= borPolyBoxL)) && !((modX >= cdBoxL)||(modY >= cdBoxL)||(modZ >= cdBoxL)) && !(Math.sqrt(ySquared + zSquared) <= polyCylRad)
  end
  ...
end

Once you've got all of these predicates as their own method, you can create a method to determine the material, like so:

def material
  [
    [:polyCylinder, 'poly'],
    [:borPolyBox, 'borPoly'],
    [:cadmiumShield, 'cd'],
    [:leadShield, 'pb'],
  ].each do |method, name|
    return name if send(method)
  end
  nil
end

And one for the position:

def position
  [@x, @y, @z] if material
end

Continue along this line until nothing is left but a bag of teeny, focused methods.

Wayne Conrad
  • 103,207
  • 26
  • 155
  • 191
  • Thank you for this! Everything is an object is a weird concept. – morb Nov 30 '10 at 22:47
  • I have built around this system and it works beautifully, thanks for the advice! I will definitely use this when dealing with complicated logic in the future. – morb Dec 01 '10 at 00:27
1

Change all and and or to && and ||.

Never seen anyone actually use array.at(index) instead of array[index] before.

I also recommend against *args in favor of a Hash parameter as a kind of named parameters

def test(params)
  x = params[:x] || raise("You have to provide x!")
  y = params[:y] || raise("You have to provide y!")
  z = params[:z] || raise("You have to provide z!")
  puts x, y, z
end

and call it with (Ruby 1.9+ syntax)

test({x: 42, y: 4711, z: 93})

42
4711
93

Jonas Elfström
  • 30,834
  • 6
  • 70
  • 106
  • If you use `fetch`, you won't need to `raise` if the key doesn't exist. – Andrew Grimm Nov 30 '10 at 22:27
  • @Andrew Yes but `IndexError: key not found` is not very informative. – Jonas Elfström Nov 30 '10 at 22:32
  • This was one of the features that attracted me to ruby. I forgot about hash parameters, thanks, going to play with that now. – morb Nov 30 '10 at 22:35
  • @morb it should have because they are the same. "array.at(index) ... See also Array#[]." - http://ruby-doc.org/ruby-1.9/classes/Array.html#M000696 – Jonas Elfström Nov 30 '10 at 22:36
  • Yes, that is one of the sites I have been using to learn. I don't have a particulary deep knowledge of programming so when I saw that there was two ways of acessing information from an array, I just assumed that there was some deeper reason for it and was happy when I got an output. – morb Nov 30 '10 at 22:43
  • @Jonas: You can do `params.fetch(:x) { raise "You have to provide :x"}`, though that may be a little less intuitive to people unfamiliar with `fetch`. – Andrew Grimm Nov 30 '10 at 23:24
  • Also, when printf debugging, remember to use `inspect` - that way, you know whether you were passed strings or integers! – Andrew Grimm Nov 30 '10 at 23:26
  • printf debugging (I think I know what you are going for but not in this context)? Sending a block to fetch or using || with hash[key] doesn't seem all that far apart to me. – Jonas Elfström Dec 01 '10 at 00:00
  • @Jonas: With the printf debugging, I meant you should use `puts [x, y, z].inspect` rather than `puts x, y, z` . – Andrew Grimm Dec 01 '10 at 23:00
  • @Jonas: If you're unfamiliar with the term "printf debugging", have a look at [this answer](http://stackoverflow.com/questions/189562/what-is-the-proper-name-for-doing-debugging-by-adding-print-statements/189570#189570) – Andrew Grimm Dec 01 '10 at 23:06
  • Or `p [x, y, z]`. `p` is a built-in method that prints its arguments, inspected, to stdout. – Wayne Conrad Dec 02 '10 at 15:41
0

Try using && instead of and, || instead of or and ! instead of not.

Your issue is probably a precedence one - read this article for more information.

david4dev
  • 4,854
  • 2
  • 28
  • 35
  • Or just be incredibly religious about the use of parentheses. – philosodad Nov 30 '10 at 21:30
  • it helps me read better, and I have gone through all permutations of the operators. I only found out today that you could use and and or, I am a Ruby noob. – morb Nov 30 '10 at 22:30
  • I really did test && and ||, their precedence didn't seem to matter. – morb Nov 30 '10 at 22:59
0

Looking at your code, the way you are setting this up is to have the four "material" variables as booleans. Then you feed those bools into an if-elsif-else block.

The problem with this is that the very first if that returns true is going to exit the if-elsif-else block. If that's what you mean by keeping the first value it comes to, than that's an extremely predictable outcome.

philosodad
  • 1,808
  • 14
  • 24
  • philosodad: Thanks, there is much to learn. I thought that was the case. – morb Nov 30 '10 at 22:28
  • Oh, no actually. That is part of ythe logic of the program, it works outwards from a center point testing wether the position (array given as arg) is in a particular domain, and so that if else bloack is *supposed* to exit on the first true value. – morb Nov 30 '10 at 22:55