meaning of "<>" in ADT specification - abstract-data-type

In reading "Data Structures Using C" (Tenenbaum, Langsam, Augenstein), I've come across an unfamiliar operator in an explanation of abstract data types.
Here's a part specifying rational as an ADT (pg. 14):
"
/* value definition*/
abstract typedef <integer, integer> RATIONAL;
condition RATIONAL[1] <> 0;
/*operator definition*/
abstract RATIONAL makerational(a,b)
int a,b;
precondition b<>0;
postcondition makerational[0] == a;
makerational[1] == b;
abstract RATIONAL add(a,b)
RATIONAL a,b;
postcondition add[1] == a[1] * b[1]
postcondition add[0] == a[0] * b[1] + b[0] * a[1];
..."
My understanding is that the overall goal here is to define a rational data type for input integers a over b in terms of legal operations. However, the use of the <> operator on lines 4 and 9 is not clear. What's the meaning of "<>" in this context?
A quick search through the stacks returned no relevant results, but I apologize in advance for a possible repeat.

Related

What are the differences between the unary minus and unary tilde minus operators in ocaml?

I'm learning OCaml, and the docs and books I'm reading aren't very clear on some things.
In simple words, what are the differences between
-10
and
~-10
To me they seem the same. I've encountered other resources trying to explain the differences, but they seem to explain in terms that I'm not yet familiar with, as the only thing I know so far are variables.
In fact, - is a binary operator, so some expression can be ambigous : f 10 -20 is treated as (f 10) - 20. For example, let's imagine this dummy function:
let f x y = (x, y)
If I want produce the tuple (10, -20) I naïvely would write something like that f 10 -20 which leads me to the following error:
# f 10 -20;;
Error: This expression has type 'a -> int * 'a
but an expression was expected of type int
because the expression is evaluated as (f 10) - 20 (so a substract over a function!!) so you can write the expression like this: f 10 (-20), which is valid or f 10 ~-20 since ~- (and ~+ and ~-. ~+. for floats) are unary operators, the predecense is properly respected.
It is easier to start by looking at how user-defined unary (~-) operators work.
type quaternion = { r:float; i:float; j:float; k:float }
let zero = { r = 0.; i = 0.; j = 0.; k = 0. }
let i = { zero with i = 1. }
let (~-) q = { r = -.q.r; i = -.q.i; j = -. q.j; k = -. q.k }
In this situation, the unary operator - (and +) is a shorthand for ~- (and ~+) when the parsing is unambiguous. For example, defining -i with
let mi = -i
works because this - could not be the binary operator -.
Nevertheless, the binary operator has a higher priority than the unary - thus
let wrong = Fun.id -i
is read as
let wrong = (Fun.id) - (i)
In this context, I can either use the full form ~-
let ok' = Fun.id ~-i
or add some parenthesis
let ok'' = Fun.id (-i)
Going back to type with literals (e.g integers, or floats), for those types, the unary + and - symbol can be part of the literal itself (e.g -10) and not an operator. For instance redefining ~- and ~+ does not change the meaning of the integer literals in
let (~+) = ()
let (~-) = ()
let really = -10
let positively_real = +10
This can be "used" to create some quite obfuscated expression:
let (~+) r = { zero with r }
let (+) x y = { r = x.r +. y.r; i = x.i +. y.i; k = x.k +. x.k; j =x.k +. y.j }
let ( *. ) s q = { r = s *. q.r; i = s *. q.i; j = s *. q.j; k = s *. q.k }
let this_is_valid x = +x + +10. *. i
OCaml has two kinds of operators - prefix and infix. The prefix operators precede expressions and infix occur in between the two expressions, e.g., in !foo we have the prefix operator ! coming before the expression foo and in 2 + 3 we have the infix operator + between expressions 2 and 3.
Operators are like normal functions except that they have a different syntax for application (aka calling), whilst functions are applied to an arbitrary number of arguments using a simple syntax of juxtaposition, e.g., f x1 x2 x3 x41, operators can have only one (prefix) or two (infix) arguments. Prefix operators are very close to normal functions, cf., f x and !x, but they have higher precedence (bind tighter) than normal function application. Contrary, the infix operators, since they are put between two expressions, enable a more natural syntax, e.g., x + y vs. (+) x y, but have lower precedence (bind less tight) than normal function application. Moreover, they enable chaining several operators in a row, e.g., x + y + z is interpreted as (x + y) + z, which is much more readable than add (add (x y) z).
Operators in OCaml distinguished purely syntactically. It means that the kind of an operator is fully defined by the first character of that operator, not by a special compiler directive, like in some other languages (i.e., there is not infix + directive in OCaml). If an operator starts with the prefix-symbol sequence, e.g., !, ?#, ~%, it is considered as prefix and if it starts with an infix-symbol then it is, correspondingly, an infix operator.
The - and -. operators are treated specially and can appear both as prefix and infix. E.g., in 1 - -2 we have - occurring both in the infix and prefix positions. However, it is only possible to disambiguate between the infix and the prefix versions of the - (and -.) operators when they occur together with other operators (infix or prefix), but when we have a general expression, the - operator is treated as infix. E.g., max 0 -1 is interpreted as (max 0) - 1 (remember that operator has lower precedence than function application, therefore when they two appear with no parentheses then functions are applied first and operators after that). Another example, Some -1, which is interpreted as Some - 1, not as Some (-1). To disambiguate such code, you can use either the parentheses, e.g., max 0 (-1), or the prefix-only versions, e.g, Some ~-1 or max 0 ~-1.
As a matter of personal style, I actually prefer parentheses as it is usually hard to keep these rules in mind when you read the code.
1) Purists will say that functions in OCaml can have only one argument and f x1 x2 x3 x4 is just ((f x1) x2) x3) x4, which would be a totally correct statement, but a little bit irrelevant to the current discussion.

When should you use plus, minus, times functions instead of using the operators in Kotlin?

I cannot think of a case where using the Kotlin built-in plus, minus, times etc. functions would return a different result from just using the corresponding operators (+, -, *). Why would you ever want to use these functions in your Kotlin code?
Just in case you aren't aware, these are operator overloads. The named functions are how the operators' functionality is defined.
There is a case for using the named versions. The function calls don't pay attention to operator precedence and are evaluated sequentially if you chain them.
val x = 1
val y = 2
val z = 3
println(x + y * z) // equivalent to 1 + (2 * 3) -> 7
println(x.plus(y).times(z)) // equivalent to (1 + 2) * 3 -> 9
This could be clearer than using the operators if you have a lot of nested parentheses, depending on the context of the type of math you're doing.
result = ((x - 7) * y - z) * 10
// vs
result = x.minus(7).times(y).minus(z).times(10)
It's not really applicable for basic algebra like this, but you might have classes with operator overloads where the logic can be more easily reasoned through with the sequential approach.
By explicitly stating function names you can perform a safe call on a nullable number which cannot be done with an operator.
For example:
fun doubleOrZero(num: Int?) : Int {
return num?.times(2) ?: 0
}

Testing bignum arithmetic

I'm writing an arbitrary precision rational number package, which I'll need to test for correctness and efficiency. Of course I could put together an ad hoc set of tests myself, but since I'm far from the first to be doing this, I figure it's worth asking: can anyone recommend an existing set of tests I could use?
Edit: I ended up writing a test routine that each time around the loop, generates three random numbers and verifies that various arithmetic identities hold. It's found several bugs in the numeric code so far. Here's the actual code:
for (i = 0;; i++)
{
mem = memlo;
printf(fmtw "\r", i);
a = rndnum();
b = rndnum();
c = rndnum();
// Equality
test(eq(a, a));
test(!eq(a, b) || !eq(b, c) || eq(a, c));
// Addition
test(eq(add(add(a, b), c), add(a, add(b, c))));
test(eq(add(a, b), add(b, a)));
test(eq(add(a, zero), a));
// Subtraction
test(eq(sub(add(a, b), b), a));
test(sub(a, a) == zero);
test(eq(sub(a, b), add(a, sub(zero, b))));
// Multiplication
test(eq(mul(mul(a, b), c), mul(a, mul(b, c))));
test(eq(mul(a, b), mul(b, a)));
test(eq(mul(a, one), a));
test(eq(mul(a, add(b, c)), add(mul(a, b), mul(a, c))));
// Division
test(b == zero || eq(div_(mul(a, b), b), a));
test(a == zero || div_(a, a) == (one));
test(b == zero || eq(div_(a, b), mul(a, div_(one, b))));
test(c == zero
|| eq(div_(sub(a, b), c), sub(div_(a, c), div_(b, c))));
// I/O
test(eq(a, roundtrip(a)));
}
Try looking at unit tests of open source rational implementations. Ruby's rational supports arbitrary precision, though only a few tests in test/ruby/test_rational2.rb push past 32 bits. For instance:
assert_equal(Rational(2305842940494218450, 1152921470247108503),
Rational(1073741789, 1073741827) + Rational(1073741827, 1073741789))
Similarly for Python's test_fractions.py:
self.assertTypedEquals(10**23, 10**22 // F(1, 10))
GNU MPL has some rational unit tests mostly based on random numbers.
The IMath package has a good set of tests, such as:
qadd:-14,9/2,-20:-19/2
qadd:-60,=1,43/2:-120
qadd:375/18696391582109365451,-131/32949770573031503434,166/56750232802998421883:9906936667630486913669/616041813174061083543307811398267458734
qadd:615/80348516296708248277,=1,=2:1230/80348516296708248277
It's not obvious to me how other open source rational bignum packages like Scheme and Sage are tested, but if you're motivated their tests should exist somewhere.

Why do most programming languages only have binary equality comparison operators?

In natural languages, we would say "some color is a primary color if the color is red, blue, or yellow."
In every programming language I've seen, that translates into something like:
isPrimaryColor = someColor == "Red" or someColor == "Blue" or someColor == "Yellow"
Why isn't there a syntax that more closely matches the English sentence. After all, you wouldn't say "some color is a primary color if that color is red, or that color is blue, or that color is yellow."
I realize simply isPrimaryColor = someColor == ("Red" or "Blue" or "Yellow") because instead of Red Blue and Yellow they could be boolean statement in which case boolean logic applies, but what about something like:
isPrimaryColor = someColor ( == "Red" or == "Blue" or == "Yellow")
As an added bonus that syntax would allow for more flexibility, say you wanted to see if a number is between 1 and 100 or 1000 and 2000, you could say:
someNumber ((>= 1 and <=100) or (>=1000 and <=2000))
Edit:
Very interesting answers, and point taken that I should learn more languages. After reading through the answers I agree that for strictly equality comparison something similar to set membership is a clear and concise way of expressing the same thing (for languages that have language support for concise inline lists or sets and testing membership)
One issues that came up is that if the value to compare is the result of an expensive calculation a temporary variable would need to be (well, should be) created. The other issue is that there may be different evaluations that need to be checked, such as "the result of some expensive calculation should be prime and between 200 and 300"
These scenarios are also covered by more functional languages (though depending on the language may not be more concise), or really any language that can take a function as a parameter. For instance the previous example could be
MeetsRequirements(GetCalculatedValue(), f(x):x > 200, f(x):x < 300, IsPrime)
I think that most people consider something like
isPrimaryColor = ["Red", "Blue", "Yellow"].contains(someColor)
to be sufficiently clear that they don't need extra syntax for this.
In python you can do something like this:
color = "green"
if color in ["red", "green", "blue"]:
print 'Yay'
It is called in operator, which tests for set membership.
In perl 6 you could do this with junctions:
if $color eq 'Red'|'Blue'|'Green' {
doit()
}
Alternately you could do it with the smart match operator (~~). The following is roughly equivalent to python's if value in list: syntax, except that ~~ does a lot more in other contexts.
if ($color ~~ qw/Red Blue Green/) {
doit()
}
The parens also make it valid perl 5 (>=5.10); in perl 6 they're optional.
In Haskell, it is easy to define a function to do this:
matches x ps = foldl (||) False $ map (\ p -> p x) ps
This function takes a value list of predicates (of type a -> Bool) and returns True if any of the the predicates match the value.
This allows you to something like this:
isMammal m = m `matches` [(=="Dog"), (=="Cat"), (=="Human")]
The nice thing is that it doesn't have to just be equality, you can use anything with the correct type:
isAnimal a = a `matches` [isMammal, (=="Fish"), (=="Bird")]
Ruby
Contained in list:
irb(main):023:0> %w{red green blue}.include? "red"
=> true
irb(main):024:0> %w{red green blue}.include? "black"
=> false
Numeric Range:
irb(main):008:0> def is_valid_num(x)
irb(main):009:1> case x
irb(main):010:2> when 1..100, 1000..2000 then true
irb(main):011:2> else false
irb(main):012:2> end
irb(main):013:1> end
=> nil
irb(main):014:0> is_valid_num(1)
=> true
irb(main):015:0> is_valid_num(100)
=> true
irb(main):016:0> is_valid_num(101)
=> false
irb(main):017:0> is_valid_num(1050)
=> true
So far, nobody has mentioned SQL. It has what you are suggesting:
SELECT
employee_id
FROM
employee
WHERE
hire_date BETWEEN '2009-01-01' AND '2010-01-01' -- range of values
AND employment_type IN ('C', 'S', 'H', 'T') -- list of values
COBOL uses 88 levels to implement named values, named groups of values
and named ranges of values.
For example:
01 COLOUR PIC X(10).
88 IS-PRIMARY-COLOUR VALUE 'Red', 'Blue', 'Yellow'.
...
MOVE 'Blue' TO COLOUR
IF IS-PRIMARY-COLOUR
DISPLAY 'This is a primary colour'
END-IF
Range tests are covered as follows:
01 SOME-NUMBER PIC S9(4) BINARY.
88 IS-LESS-THAN-ZERO VALUE -9999 THRU -1.
88 IS-ZERO VALUE ZERO.
88 IS-GREATER-THAN-ZERO VALUE 1 THRU 9999.
...
MOVE +358 TO SOME-NUMBER
EVALUATE TRUE
WHEN IS-LESS-THAN-ZERO
DISPLAY 'Negative Number'
WHEN IS-ZERO
DISPLAY 'Zero'
WHEN IS-GREATER-THAN-ZERO
DISPLAY 'Positive Number'
WHEN OTHER
DISPLAY 'How the heck did this happen!'
END-EVALUATE
I guess this all happened because COBOL was supposed to emulate English
to some extent.
You'll love Perl 6 because it has:
chaining comparison operators:
(1 <= $someNumber <= 100) || (1000 <= $someNumber <= 2000))
junctive operators:
$isPrimaryColor = $someColor ~~ "Red" | "Blue" | "Yellow"
And you can combine both with ranges:
$someNumber ~~ (1..100) | (1000..2000)
Python actually gives you the ability to do the last thing quite well:
>>> x=5
>>> (1<x<1000 or 2000<x<3000)
True
In Python you can say ...
isPrimaryColor = someColor in ('Red', 'Blue', 'Yellow')
... which I find more readable than your (== "Red" or == "Blue") syntax. There's a few reasons to add syntax support for a language feature:
Efficiency: Not a reason here, since there's no speed improvement.
Functionality: Also not a concern; there's nothing you can do in the new syntax that you can't do in the old.
Legibility: Most languages handle the case where you're checking the equality of multiple values just fine. In other cases (e.g., someNumber (> 1 and < 10)) it might be more useful, but even then it doesn't buy you much (and Python allows you to say 1 < someNumber < 10, which is even clearer).
So it's not clear the proposed change is particularly helpful.
My guess would be that languages are designed by force of habit. Early languages only would have had binary comparison operators because they are simpler to implement. Everyone got used to saying (x > 0 and x < y) until language designers didn't ever bother to support the common form in mathematics, (0 < x < y).
In most languages a comparison operator returns a boolean type. In the case of 0 < x < y, if this is interpreted as (0 < x) < y it would be meaningless, since < does not make sense for comparing booleans. Therefore, a new compiler could interpret 0 < x < y as tmp:=x, 0 < tmp && tmp < y without breaking backward compatibility. In the case of x == y == z, however, if the variables are already booleans, it is ambiguous whether this means x == y && y == z or (x == y) == z.
In C# I use the following extension method so that you can write someColor.IsOneOf("Red", "Blue", "Yellow"). It is less efficient than direct comparison (what with the array, loop, Equals() calls and boxing if T is a value type), but it sure is convenient.
public static bool IsOneOf<T>(this T value, params T[] set)
{
object value2 = value;
for (int i = 0; i < set.Length; i++)
if (set[i].Equals(value2))
return true;
return false;
}
Icon has the facility you describe.
if y < (x | 5) then write("y=", y)
I rather like that aspect of Icon.
In C#:
if ("A".IsIn("A", "B", "C"))
{
}
if (myColor.IsIn(colors))
{
}
Using these extensions:
public static class ObjectExtenstions
{
public static bool IsIn(this object obj, params object [] list)
{
foreach (var item in list)
{
if (obj == item)
{
return true;
}
}
return false;
}
public static bool IsIn<T>(this T obj, ICollection<T> list)
{
return list.Contains(obj);
}
public static bool IsIn<T>(this T obj, IEnumerable<T> list)
{
foreach (var item in list)
{
if (obj == item)
{
return true;
}
}
return false;
}
}
You'll have to go a bit down the abstraction layer to find out the reason why. x86's comparison/jump instructions are binary (since they can be easily computed in a few clock cycles), and that's the way things have been.
If you want, many languages offer an abstraction for that. In PHP, for example you could use:
$isPrimaryColor = in_array($someColor, array('Red', 'White', 'Blue'));
I don't see an Objective-C answer yet. Here is one:
BOOL isPRimaryColour = [[NSSet setWithObjects: #"red", #"green", #"blue", nil] containsObject: someColour];
The question is reasonable, and I wouldn't regard the change as syntactic sugar. If the value being compared is the result of computation, it would be nicer to say:
if (someComplicatedExpression ?== 1 : 2 : 3 : 5)
than to say
int temp;
temp = someComplicatedExpression;
if (temp == 1 || temp == 2 || temp == 3 || temp == 5)
particularly if there was no other need for the temp variable in question. A modern compiler could probably recognize the short useful lifetime of 'temp' and optimize it to a register, and could probably recognize the "see if variable is one of certain constants" pattern, but there'd be no harm in allowing a programmer to save the compiler the trouble. The indicated syntax wouldn't compile on any existing compiler, but I don't think it would be any more ambiguous than (a+b >> c+d) whose behavior is defined in the language spec.
As to why nobody's done that, I don't know.
I'm reminded of when I first started to learn programming, in Basic, and at one point I wrote
if X=3 OR 4
I intended this like you are describing, if X is either 3 or 4. The compiler interpreted it as:
if (X=3) OR (4)
That is, if X=3 is true, or if 4 is true. As it defined anything non-zero as true, 4 is true, anything OR TRUE is true, and so the expression was always true. I spent a long time figuring that one out.
I don't claim this adds anything to the discussion. I just thought it might be a mildly amusing anecdote.
As a mathematician, I would say that the colour is primary if and only if it is a member of the set {red, green, blue} of primary colours.
And this is exactly how you could say in Delphi:
isPrimary := Colour in [clRed, clGreen, clBlue]
In fact, I employ this technique very often. Last time was three days ago. Implementing my own scripting language's interpreter, I wrote
const
LOOPS = [pntRepeat, pntDoWhile, pntFor];
and then, at a few lines,
if Nodes[x].Type in LOOPS then
The Philosophical Part of the Question
#supercat, etc. ("As to why nobody's done that, I don't know."):
Probably because the designers of programming languages are mathematicians (or, at least, mathematically inclined). If a mathematician needs to state the equality of two objects, she would say
X = Y,
naturally. But if X can be one of a number of things A, B, C, ..., then she would define a set S = {A, B, C, ...} of these things and write
X ∈ S.
Indeed, it is extremely common that you (mathematicians) write X ∈ S, where S is the set
S = {x ∈ D; P(x)}
of objects in some universe D that has the property P, instead of writing P(X). For instance, instead of saying "x is a positive real number", or "PositiveReal(x)", one would say x ∈ ℝ⁺.
It's because programming languages are influenced by mathematics, logic and set theory in particular. Boolean algebra defines ∧, ∨ operators in a way that they do not work like spoken natural language. Your example would be written as:
Let p(x) be unary relation which holds if and only if x is a primary color
p(x) ⇔ r(x) ∨ g(x) ∨ b(x)
or
p(x) ⇔ (x=red) ∨ (x=green) ∨ (x=blue)
As you see, it's pretty similar to notation that would be used in programming language. As mathematics provide strong theoretic foundations, programming languages are based on mathematics rather than natural language which always leaves a lot of space for interpretation.
EDIT: Above statement could be simplified by using set notation:
p(x) ⇔ x ∈ {red, green, blue}
and indeed, some programming languages, most notably Pascal, included set, so you could type:
type
color = (red, green, blue, yellow, cyan, magenta, black, white);
function is_primary (x : color) : boolean;
begin
is_primary := x in [red, green, blue]
end
But sets as a language feature didn't catch on.
PS. Sorry for my imperfect English.
The latter examples you give are effectively syntactic sugar, they'd have to evaluate to the same code as the longer form as at some point the executed code has to compare your value with each of the conditions in turn.
The array comparison syntax, given in several forms here, closer and I suspect there are other languages which get even closer.
The main problem with making syntax closer to natural language is that the latter is not just ambiguous, it's hideously ambiguous. Even keeping ambiguity to a minimum we still manage to introduce bugs into our apps, can you imagine what it would be like if you programmed in natural english?!
Just to add to language examples
Scheme
(define (isPrimaryColor color)
(cond ((member color '(red blue yellow)) #t)
(else #f)))
(define (someNumberTest x)
(cond ((or (and (>= x 1) (<= x 100)) (and (>= x 10000 (<= x 2000))) #t)
(else #f)))
Two possibilities
Java
boolean isPrimary = Arrays.asList("red", "blue", "yellow").contains(someColor);
Python
a = 1500
if 1 < a < 10 or 1000 < a < 2000:
print "In range"
This can be replicated in Lua with some metatable magic :D
local function operator(func)
return setmetatable({},
{__sub = function(a, _)
return setmetatable({a},
{__sub = function(self, b)
return f(self[1], b)
end}
)
end}
)
end
local smartOr = operator(function(a, b)
for i = 1, #b do
if a == b[i] then
return true
end
end
return false
end)
local isPrimaryColor = someColor -smartOr- {"Red", "Blue", "Either"}
Note: You can change the name of -smartOr- to something like -isEither- to make it even MORE readable.
Languages on computers compare as binary because they are all for a machine that uses binary to represent information. They were designed using similar logic and with broadly similar goals. The English language wasn't designed logically, designed to describe algorithms, and human brains (the hardware it runs on) aren't based on binary. They're tools designed for different tasks.

compute results and mux or not

Using pseudo code here. Are there pros and cons to these styles:
Say you have an alu that can do add, and, or and xor. Is it better to have code that computes the possible answers all the time then select the answer based on the opcode (in this case a one hot):
alu_add = a + b;
alu_and = a & b;
alu_or = a | b;
alu_xor = a ^ b;
...
if(opcode[0]) alu_out = alu_add;
else if(opcode[1]) alu_out = alu_and;
else if(opcode[2]) alu_out = alu_or;
else if(opcode[3]) alu_out = alu_xor;
An alternative would be to code it like this:
if(opcode[0]) alu_out = a + b;
else if(opcode[1]) alu_out = a & b;
else if(opcode[2]) alu_out = a | b;
else if(opcode[3]) alu_out = a ^ b;
I have also seen it as:
alu_add = a + b;
alu_and = a & b;
alu_or = a | b;
alu_xor = a ^ b;
...
alu_out =
( 8{opcode[0]} & alu_add ) |
( 8{opcode[1]} & alu_and ) |
( 8{opcode[2]} & alu_or ) |
( 8{opcode[3]} & alu_xor );
Are there pros and cons to either method or do they come out about the same in the end?
Think about this in terms of levels of logic and readability. The first two forms are fine in terms of readability, but both embody priority unnecessarily and will result in more levels of logic. The third form is also not so wonderful by either of these metrics. Lastly, there's no apparent reason to use one-hot coding here over binary coding. Here's how I'd code this:
parameter ALU_ADD = 2'b00;
parameter ALU_AND = 2'b01;
parameter ALU_OR = 2'b10;
parameter ALU_XOR = 2'b11;
reg [1:0] opcode; // 2 bits for binary coding vs. 4 for one-hot
// and later, in your always block:
case (opcode) // synopsys parallel_case
ALU_ADD: alu_out = a + b;
ALU_AND: alu_out = a & b;
ALU_OR: alu_out = a | b;
ALU_XOR: alu_out = a ^ b;
endcase
Here I've explicitly assigned values to the ALU opcodes, avoiding "magic numbers" and making it easier for others to understand what's happening. I've also used the case statement and applied a directive that tells my synthesis tool that no more than one expression can be matched, so no priority encoder will be inferred. I didn't name the intermediate signals (alu_add etc.) because these are trivial operations, but I frequently do so when I want convenient access to those signals (seeing their values after simulation in a waveform viewer for example).
You can learn more about using case statements effectively in this article from the excellent Sunburst Design site (no affiliation, just a former student).
Finally, about your question, "Is it better to have code that computes the possible answers all the time then select the answer based on the opcode" -- remember that Verilog is a hardware description language. All the implementations on this page are computing everything all the time anyway. Where they differ is in levels of logic and readability. Take a look at this page, which shows that my implementation has 1 level of logic beyond the operations themselves, where the if-else implementation has 3 additional levels of logic.
The first two will give the same logic, but you'll get a latch on alu_out because your if else block (your priority encoder) has no final else. (This is true for verilog anyway). If your timing is tight, you may have issues with the long paths that a priority encoder implies.
On the 3rd version, you'll get a more 'parallel' structure which may be better for timing. It won't latch.
In all three versions, each of the four operations will be clattering away nomatter what the opcode. This will have power implications.
It's my opinion that the first version wins for clarity, and you can get at each of the separate operations in your waveform viewer when debugging. There's no point in coding up something that is not as easy to read unless timing, area or power constraints are hurting.