Fraction
context (or Fraction-NoDecimals
if you prefer), and enable Matrices in that context. You do that by using
Context()->parens->set("[" => {formMatrix => 1});This says that
[...]
is allowed to be used to form matrices, and that is needed for the parsing of student answers as matrices. Although you can always use Matrix()
as a problem author, student answers can't produce matrices unless the context has this formMatrix
value set. This is needed even if you are using ans_array
as the answer array converts the student answer to use this notation and then parses it. WIthout that, you will get a message something like "Your answer isn't a matrix, it looks like a list of lists of numbers" or some such thing.
Without seeing the actual code you are using, that is the best I can do.
Davide
In terms of student answers, a matrix with mixed Reals and Fractions also is OK. Each entry in the array of student answers will be computed as a Real or Fraction (depending on whether /
is used), and combined into the final student Matrix. Reals and Fractions can be compared without error messages, so this can be compared to the correct Matrix just fine.
You may wish to use the Fraction-NoDecimals
if you want to force students to enter fractions, but you could go either way.
Finally, note that
@W=([1,2,3,4],[3,3,4,5],[-1,-3,2,4]);is not actually using any MathObject yet (it is only when you do
$R = Matrix(@W)
that you get MathObjects as the entries. So technically, these are Perl reals, not MathObject Reals, and certainly wouldn't be Fractions at this point, since there are no MathObject constructors or Compute()
calls.
For (3), the result will be a Fraction. Combining Fractions with integer-values Reals (or Perl reals) will result in Fraction objects.
For (4), The only reason to declare the Context within a subroutine is if you need to change it. The Context is global, so the subroutine can use it just like the outer code, and if you set the Context within a subroutine, it is set globally and will remain in effect after the subroutine ends. Note also that setting the context will cause any changes you have made to be lost (even if you change to same context).
Hope that helps.
Davide
-
First, the line
Context()->parens->set("[" => {formMatrix => 1});
must come after selecting the Fraction context, so it should beContext("Fraction"); Context()->parens->set("[" => {formMatrix => 1});
Also, there is no need to select Numeric context since you don't actually create any MathObjects while in that context (the$nrows
and$ncols
are Perl integers, not MathObjects).
As discussed above, you don't need to make integers into fractions, though you can if you want, so
@W=([1,2,3,4],[3,3,4,5],[-1,-3,2,4]);is fine. (The reason this didn't work for you is actually later in the code; see below.)
Rather than using
my $i = $_[0]; my $j = $_[1];you could use the shorter
my ($i,$j) = @_;to get the first two element of the arguments array (
@_
) as $i
and $j
.
You can do swap of two values without having to create a temporary variable. To swap
$a
and $b
, you can do
($a,$b) = ($b,$a);so you could do
($W[$i][$k],$W[$j][$k]) = ($W[$j][$k],$W[$i][$k]);to swap these two entries. Combining this with the previous item shortens your rowswap macro to
sub rowsswap { my ($i,$j) = @_; for my $k (0..$ncols-1) { ($W[$i][$k],$W[$j][$k]) = ($W[$j][$k],$W[$i][$k]); } }
You can multiply a variable by a value using
*=
, so $W[$i][$k] = $lambda*$W[$i][$k];
could be shortened to $W[$i][$k] *= $lambda;
(though not everyone likes this notation). Similarly, $W[$i][$k] = $W[$i][$k] + $lambda*$W[$j][$k];
could be replaced by $W[$i][$k] += $lambda*$W[$j][$k];
making your second two row macros be
sub rowmult { my ($i,$lambda) = @_; for my $k (0..$ncols-1) {$W[$i][$k] *= $lambda} } sub rowadd { my ($i,$j,$lambda) = @_; for my $k (0..$ncols-1) {$W[$i][$k] += $lambda*$W[$j][$k]} }The reason you had trouble with the reals versus fractions is in your pivoting algorithm where you use
1/$W[$ipivot][$jpivot]
. Note that this only produces a fraction if $W[$ipivot][$jpivot]
is a fraction; if it is a Perl real, then it is just Perl division and produces a Perl decimal real, not a MathObject Fraction.
You can fix this by using 1/Fraction($W[$ipivot][$jpivot])
instead. Then if $W[$ipivot][$jpivot]
is a Perl integer, it will be made into a Fraction object, and one over it will again be a fraction. If it is already a Fraction, this will just return the original Fraction, no harm done. So you can start with a matrix of reals, and introduce Fractions into it only when needed.
Anyway, I think that should simplify your code a bit, and fix up a few minot details.
Davide
ans_array
. I submitted a patch last week, and it is in the 2.7 release. The changes are in pg/lib/Value/AnswerCheckers.pm. I think you should be able to replace your current copy with this version without trouble (but save your earlier copy just in case).Davide
BTW, these are not hashes, but array references. Hashes are collections of name/value pairs stored in variables starting with
%
rather than @
. You are just using arrays, not hashes.Davide