Define a primitive data type: Difference between revisions
m →{{header|Sidef}}: added a new method (get_value) |
Omit Axe |
||
Line 1,787: | Line 1,787: | ||
End Structure</lang> |
End Structure</lang> |
||
{{omit from|AWK}} |
{{omit from|AWK}} |
||
{{omit from|Axe}} |
|||
{{omit from|BBC BASIC}} |
{{omit from|BBC BASIC}} |
||
{{omit from|bc}} |
{{omit from|bc}} |
Revision as of 05:11, 2 July 2015
You are encouraged to solve this task according to the task description, using any language you may know.
Demonstrate how to define a type that behaves like an integer but has a lowest valid value of 1 and a highest valid value of 10. Include all bounds checking you need to write, or explain how the compiler or interpreter creates those bounds checks for you.
Ada
<lang ada>type My_Type is range 1..10;</lang> The compiler identifies the range of valid values from the range specification 1..10 and automatically builds in bounds checking where it is needed. The compiler is smart enough to omit bounds checking when it is not needed. <lang ada>A : My_Type := 3; B : My_Type := A;</lang> The compiler will omit bounds checking for the assignment of A to B above because both values are of My_Type. A cannot hold a value outside the range of 1..10, therefore the assignment cannot produce an out of bounds result.
ALGOL 68
Built in or standard distribution routines
Bounded data types are not part of standard ALGOL 68, but can be implemented.
Implementation example
<lang algol68> # assume max int <= ABS - max negative int #
INT max bounded = ( LENG max int * max int > long max int | ENTIER sqrt(max int) | max int ); MODE RANGE = STRUCT(INT lwb, upb); MODE BOUNDED = STRUCT(INT value, RANGE range); FORMAT bounded repr = $g"["g(-0)":"g(-0)"]"$; # Define some useful operators for looping over ranges # OP LWB = (RANGE range)INT: lwb OF range, UPB = (RANGE range)INT: upb OF range, LWB = (BOUNDED bounded)INT: lwb OF range OF bounded, UPB = (BOUNDED bounded)INT: upb OF range OF bounded; PROC raise exception = ([]STRING args)VOID: ( put(stand error, ("exception: ",args, newline)); stop ); PROC raise not implemented error := ([]STRING args)VOID: raise exception(args); PROC raise bounds error := ([]STRING args)VOID: raise exception(args); PRIO MIN=9, MAX=9; OP MIN = ([]INT list)INT: ( INT out:= list[LWB list]; FOR index FROM LWB list+1 TO UPB list DO IF list[index]<out THEN out :=list[index] FI OD; out ); OP MAX = ([]INT list)INT: ( INT out:= list[LWB list]; FOR index FROM LWB list+1 TO UPB list DO IF list[index]>out THEN out :=list[index] FI OD; out ); PRIO ASSERTIN = 6; OP ASSERTIN = (INT result, []RANGE range)BOUNDED: ( BOUNDED out = (result, (MAX lwb OF range, MIN upb OF range)); IF value OF out < lwb OF range OF out THEN raise bounds error(("out of bounds", whole(result, int width)," < [",whole(MAX lwb OF range, int width),":]")) ELIF value OF out > upb OF range OF out THEN raise bounds error(("out of bounds", whole(result, int width)," > [:",whole(MIN upb OF range, int width),"]")) FI; out ), ASSERTIN = (LONG INT result, []RANGE range)BOUNDED: ( STRUCT (LONG INT value, RANGE range) out = (result, (MAX lwb OF range, MIN upb OF range)); IF value OF out < lwb OF range OF out THEN raise bounds error(("out of bounds", whole(result, long int width)," < [",whole(MAX lwb OF range, int width),":]")) ELIF value OF out > upb OF range OF out THEN raise bounds error(("out of bounds", whole(result, long int width)," > [:",whole(MIN upb OF range, int width),"]")) FI; (SHORTEN value OF out, range OF out) ), ASSERTIN = (INT result, []BOUNDED bounds)BOUNDED: result ASSERTIN range OF bounds, ASSERTIN = (LONG INT result, []BOUNDED bounds)BOUNDED: result ASSERTIN range OF bounds; INT half max int = max int OVER 2; INT sqrt max int = ENTIER sqrt (max int); OP + = (BOUNDED a, b)BOUNDED: IF ABS value OF a < half max int AND ABS value OF b < half max int THEN value OF a + value OF b ASSERTIN []BOUNDED(a,b) ELSE LENG value OF a + value OF b ASSERTIN []BOUNDED(a,b) FI, - = (BOUNDED a, b)BOUNDED: value OF a + -value OF b ASSERTIN []BOUNDED(a,b), * = (BOUNDED a, b)BOUNDED: IF ABS value OF a < sqrt max int AND ABS value OF b < sqrt max int THEN value OF a * value OF b ASSERTIN []BOUNDED(a,b) ELSE LENG value OF a * value OF b ASSERTIN []BOUNDED(a,b) FI, / = (BOUNDED a, b)REAL: value OF a / value OF b, % = (BOUNDED a, b)BOUNDED: value OF a % value OF b ASSERTIN []BOUNDED(a,b), %* = (BOUNDED a, b)BOUNDED: value OF a %* value OF b ASSERTIN []BOUNDED(a,b), ** = (BOUNDED a, INT exponent)BOUNDED: value OF a ** exponent ASSERTIN []BOUNDED(a); OP OVER = (INT value, RANGE range)BOUNDED: IF ABS lwb OF range > max bounded THEN raise bounds error(("out of bounds, ABS", whole(lwb OF range, int width)," > [:",whole(max bounded, int width),"]")); SKIP ELIF ABS upb OF range > max bounded THEN raise bounds error(("out of bounds, ABS", whole(upb OF range, int width)," > [:",whole(max bounded, int width),"]")); SKIP ELSE value ASSERTIN []RANGE(range) FI; OP INTINIT = (BOUNDED range)REAL: value OF range; OP < = (BOUNDED a, b)BOOL: value OF a < value OF b, > = (BOUNDED a, b)BOOL: value OF a > value OF b, <= = (BOUNDED a, b)BOOL: NOT ( value OF a > value OF b ), >= = (BOUNDED a, b)BOOL: NOT ( value OF a < value OF b ), = = (BOUNDED a, b)BOOL: value OF a = value OF b, /= = (BOUNDED a, b)BOOL: NOT (a = b); # Monadic operators # OP - = (BOUNDED range)BOUNDED: -value OF range ASSERTIN []BOUNDED(range), ABS = (BOUNDED range)BOUNDED: ABS value OF range ASSERTIN []BOUNDED(range); COMMENT Operators for extended characters set, and increment/decrement: OP +:= = (REF BOUNDED a, BOUNDED b)REF BOUNDED: ( a := a + b ), +=: = (BOUNDED a, REF BOUNDED b)REF BOUNDED: ( b := a + b ), -:= = (REF BOUNDED a, BOUNDED b)REF BOUNDED: ( a := a - b ), *:= = (REF BOUNDED a, BOUNDED b)REF BOUNDED: ( a := a * b ), %:= = (REF BOUNDED a, BOUNDED b)REF BOUNDED: ( a := a % b ), %*:= = (REF BOUNDED a, BOUNDED b)REF BOUNDED: ( a := a %* b ); # OP aliases for extended character sets (eg: Unicode, APL, ALCOR and GOST 10859) # OP × = (BOUNDED a, b)BOUNDED: a * b, ÷ = (BOUNDED a, b)INT: a OVER b, ÷× = (BOUNDED a, b)BOUNDED: a MOD b, ÷* = (BOUNDED a, b)BOUNDED: a MOD b, %× = (BOUNDED a, b)BOUNDED: a MOD b, ≤ = (BOUNDED a, b)BOUNDED: a <= b, ≥ = (BOUNDED a, b)BOUNDED: a >= b, ≠ = (BOUNDED a, b)BOOL: a /= b, ↑ = (BOUNDED range, INT exponent)BOUNDED: value OF range ** exponent, ÷×:= = (REF BOUNDED a, BOUNDED b)REF BOUNDED: ( a := a MOD b ), %×:= = (REF BOUNDED a, BOUNDED b)REF BOUNDED: ( a := a MOD b ), ÷*:= = (REF BOUNDED a, BOUNDED b)REF BOUNDED: ( a := a MOD b ); # BOLD aliases for CPU that only support uppercase for 6-bit bytes - wrist watches # OP OVER = (BOUNDED a, b)INT: a % b, MOD = (BOUNDED a, b)BOUNDED: a %*b, LT = (BOUNDED a, b)BOOL: a < b, GT = (BOUNDED a, b)BOOL: a > b, LE = (BOUNDED a, b)BOOL: a <= b, GE = (BOUNDED a, b)BOOL: a >= b, EQ = (BOUNDED a, b)BOOL: a = b, NE = (BOUNDED a, b)BOOL: a /= b, UP = (BOUNDED range, INT exponent)BOUNDED: range**exponent; # the required standard assignment operators # OP PLUSAB = (REF BOUNDED a, BOUNDED b)REF BOUNDED: ( a +:= b ), # PLUS # PLUSTO = (BOUNDED a, REF BOUNDED b)REF BOUNDED: ( a +=: b ), # PRUS # MINUSAB = (REF BOUNDED a, BOUNDED b)REF BOUNDED: ( a *:= b ), DIVAB = (REF BOUNDED a, BOUNDED b)REF BOUNDED: ( a /:= b ), OVERAB = (REF BOUNDED a, BOUNDED b)REF BOUNDED: ( a %:= b ), MODAB = (REF BOUNDED a, BOUNDED b)REF BOUNDED: ( a %*:= b );
END COMMENT Test:
RANGE range = RANGE(0, 10000); # override the default exception # raise bounds error := ([]STRING args)VOID: ( putf(stand error, ($g$, args, $"- exiting to except bounds error"l$)); except bounds error ); BOUNDED a, b := 0 OVER range; FOR step FROM 4 BY 4 TO UPB range DO # something for pythagoras # b := b + step OVER range; a := ENTIER sqrt( 1.5 + 2 * value OF b ) OVER range OF b; printf(($"Sum of "$, bounded repr, a * a, b * b, $" is "$, bounded repr, a * a + b * b, $l$)) OD; except bounds error: SKIP</lang>
Output:
Sum of +9[0:10000] +16[0:10000] is +25[0:10000] Sum of +25[0:10000] +144[0:10000] is +169[0:10000] Sum of +49[0:10000] +576[0:10000] is +625[0:10000] Sum of +81[0:10000] +1600[0:10000] is +1681[0:10000] Sum of +121[0:10000] +3600[0:10000] is +3721[0:10000] Sum of +169[0:10000] +7056[0:10000] is +7225[0:10000] out of bounds +12544 > [: +10000]- exiting to except bounds error
Other libraries or implementation specific extensions
As of February 2009 no open source libraries to do this task have been located.
C#
<lang csharp>public struct TinyInt {
private const int minimalValue = 1; private const int maximalValue = 10;
private readonly int value;
private TinyInt(int i) { if (minimalValue > i || i > maximalValue) { throw new System.ArgumentOutOfRangeException(); } value = i; }
public static implicit operator int(TinyInt i) { return i.value; }
public static implicit operator TinyInt(int i) { return new TinyInt(i); }
}</lang>
C++
This class relies on implicit conversions to do most int operations; however the combined operations with assignment have to be coded explicitly.
<lang cpp>#include <stdexcept>
class tiny_int { public:
tiny_int(int i): value(i) { if (value < 1) throw std::out_of_range("tiny_int: value smaller than 1"); if (value > 10) throw std::out_of_range("tiny_int: value larger than 10"); } operator int() const { return value; } tiny_int& operator+=(int i) { // by assigning to *this instead of directly modifying value, the // constructor is called and thus the check is enforced *this = value + i; return *this; } tiny_int& operator-=(int i) { *this = value - i; return *this; } tiny_int& operator*=(int i) { *this = value * i; return *this; } tiny_int& operator/=(int i) { *this = value / i; return *this; } tiny_int& operator<<=(int i) { *this = value << i; return *this; } tiny_int& operator>>=(int i) { *this = value >> i; return *this; } tiny_int& operator&=(int i) { *this = value & i; return *this; } tiny_int& operator|=(int i) { *this = value | i; return *this; }
private:
unsigned char value; // we don't need more space
};</lang>
Clojure
Use proxy on java.lang.Number so it can be used in Clojure's math operations.
<lang clojure>(defn tinyint [^long value]
(if (<= 1 value 10) (proxy [Number] [] (doubleValue [] value) (longValue [] value)) (throw (ArithmeticException. "integer overflow"))))</lang>
- Output:
user=> (+ (tinyint 1) (tinyint 9)) 10 user=> (* 4 (tinyint 6)) 24 user=> (* 4 (tinyint 6.0)) 24 user=> (/ (tinyint 3) (tinyint 5)) 3/5 user=> (tinyint 11) ArithmeticException integer overflow user=> (.doubleValue (tinyint 9.4)) 9.0
Common Lisp
The built-in integer type specifier provides range parameters. deftype
may be used to define an alias for it.
<lang lisp>(deftype one-to-ten ()
'(integer 1 10))</lang>
For a bounds check, one may use typep
(a predicate) or check-type
(signals an error if not of the type).
<lang lisp>(defun word (i)
(check-type i one-to-ten) (case i (1 "one") (2 "two") (3 "three") (4 "four") (5 "five") (6 "six") (7 "seven") (8 "eight") (9 "nine") (10 "ten")))</lang>
(Note that the above can be written without the separate check-type by using ecase
instead of case
, which signals an error when no case matches.)
To inform the compiler that a variable will be of a certain type, permitting optimizations, use a declaration:
<lang lisp>(dolist (i numbers)
(declare (type one-to-ten i)) ...)</lang>
Note, however, that the standard does not specify what happens in the event that a declaration is false (though SBCL, for example, does perform type checks on any declaration except when safety
is 0); use check-type
for portable bounds checks.
D
<lang D>import std.exception, std.string, std.traits;
/++ A bounded integral type template. Template params: - min: the minimal value - max: the maximal value - I: the type used to store the value internally +/ struct BoundedInt(alias min, alias max, I = int) {
// Static checks static assert(isIntegral!(typeof(min))); static assert(isIntegral!(typeof(max))); static assert(isIntegral!I); static assert(min < max); static assert(cast(I) max == max, "Type " ~ I.stringof ~ " cannot hold values up to " ~ max.stringof); /// The actual value stored in this struct private I _value;
/// Getter for the internal value @property I internalValue() { return _value; } /// 'alias this' to make this struct look like a built-in integer alias internalValue this; /// Constructor this(T)(T value) { opAssign(value); } /// Assignment operator void opAssign(T)(T value) if (isIntegral!T) { _value = checked(value); } /// Unary operators auto opUnary(string op)() const { return checked(mixin(op ~ "_value")); } /// Binary operators auto opBinary(string op, T)(T other) const { return checked(mixin("_value" ~ op ~ "other")); }
/// ditto auto opBinaryRight(string op, T)(T other) const if (isIntegral!T) { return checked(mixin("_value" ~ op ~ "other")); } // Bounds enforcement private I checked(T)(T value) const { enforce(value >= min && value <= max, format("Value %s is out of bounds (%s to %s).", value, min, max)); return cast(I) value; }
}
unittest {
alias MyInt = BoundedInt!(1, 10); // alias BoundInt!(1, 10) MyInt; // D < 2.061
MyInt i = 4; MyInt j = i + i; assert(j / 2 == 4); assert(2 + j == 10); assert(i < 5); assert(j > i);
}</lang>
E
<lang e>def MyNumber := 1..10
for i :MyNumber in [0, 5, 10, 15, 20, 25] {
println(i)
}</lang> (Note: The region guard, while provided with E, is entirely unprivileged code, and could be argued not to be "primitive".)
Euphoria
In Euphoria types are special functions that may be used in declaring the allowed values for a variable. A type must have exactly one parameter and should return an atom that is either true (non-zero) or false (zero). Types can also be called just like other functions. The types object, sequence, atom and integer are predefined. <lang euphoria>type My_Type(integer i)
return i >= 1 and i <= 10
end type</lang>
Fortran
The module gives an example of how a bounded integer could be implemented in Fortran (not all the needed interfaces are implemented, and only the one for the + operator are shown). Bounds are checked at run-time.
<lang fortran>module Bounded
implicit none
type BoundedInteger integer, private :: v ! we cannot allow direct access to this, or we integer, private :: from, to ! can't check the bounds! logical, private :: critical end type BoundedInteger
interface assignment(=) module procedure bounded_assign_bb, bounded_assign_bi !, & ! bounded_assign_ib end interface
interface operator(+) module procedure bounded_add_bbb !, bounded_add_bbi, & ! bounded_add_bib, bounded_add_ibb, & ! bounded_add_iib, bounded_add_ibi, & ! bounded_add_bii end interface
private :: bounded_assign_bb, bounded_assign_bi, & bounded_add_bbb
contains
subroutine set_bound(bi, lower, upper, critical, value) type(BoundedInteger), intent(out) :: bi integer, intent(in) :: lower, upper integer, intent(in), optional :: value logical, intent(in), optional :: critical
bi%from = min(lower, upper) bi%to = max(lower, upper) if ( present(critical) ) then bi%critical = critical else bi%critical = .false. end if if ( present(value) ) then bi = value end if end subroutine set_bound
subroutine bounded_assign_bb(a, b) type(BoundedInteger), intent(out) :: a type(BoundedInteger), intent(in) :: b
call bounded_assign_bi(a, b%v)
end subroutine bounded_assign_bb
subroutine bounded_assign_bi(a, b) type(BoundedInteger), intent(out) :: a integer, intent(in) :: b
if ( (a%from <= b) .and. (a%to >= b) ) then a%v = b else write(0,*) "BoundedInteger: out of bound assignment" if ( a%critical ) then stop else if ( b < a%from ) then a%v = a%from else a%v = a%to end if write(0,"(A,' (',I0, ')')") "BoundedInteger: set to nearest bound", a%v end if end if end subroutine bounded_assign_bi
function bounded_add_bbb(a, b) result(c) type(BoundedInteger) :: c type(BoundedInteger), intent(in) :: a, b
integer :: t
c%from = max(a%from, b%from) c%to = min(a%to, b%to) t = a%v + b%v if ( c%from <= t .and. c%to >= t ) then c%v = t else write(0,*) "BoundedInteger: out of bound sum" if ( a%critical .or. b%critical ) then stop else if ( t < c%from ) then c%v = c%from else c%v = c%to end if write(0,"(A,' (',I0,')')") "BoundedInteger: set to nearest bound", c%v end if end if end function bounded_add_bbb
end module Bounded</lang>
<lang fortran>program BoundedTest
use Bounded implicit none
type(BoundedInteger) :: a, b, c
call set_bound(a, 1, 10) ! if we want to stop the program if a is out of bounds... ! call set_bound(a, 1, 10, critical=.true.) call set_bound(b, 1, 10) call set_bound(c, 1, 10) ! if we want to init c to a specific value...: ! call set_bound(c, 1, 10, value=6)
a = 1 ! ok a = 4 ! ok a = -1 ! warning (a=1) a = 11 ! warning (a=10) a = 3 ! ok b = a ! ok c = a + b ! ok (3+3) c = c + a ! ok (6+3=9) c = c + b ! warning (c=10)
end program BoundedTest</lang>
Haskell
Haskell doesn't have any built-in subrange types. However, it is possible to declare arbitrary types that "behave like" any of the built-in types on the "usual" numeric etc. operations, because these operations are defined by type-classes. So we generalize the task a bit, and first declare a generic container type that supports an additional check operation. Then, we lift any operation in the base type to the container type, by executing the check after each operation:
<lang haskell>{-# OPTIONS -fglasgow-exts #-}
data Check a b = Check { unCheck :: b } deriving (Eq, Ord)
class Checked a b where
check :: b -> Check a b
lift f x = f (unCheck x) liftc f x = check $ f (unCheck x)
lift2 f x y = f (unCheck x) (unCheck y) lift2c f x y = check $ f (unCheck x) (unCheck y) lift2p f x y = (check u, check v) where (u,v) = f (unCheck x) (unCheck y)
instance Show b => Show (Check a b) where
show (Check x) = show x showsPrec p (Check x) = showsPrec p x
instance (Enum b, Checked a b) => Enum (Check a b) where
succ = liftc succ pred = liftc pred toEnum = check . toEnum fromEnum = lift fromEnum
instance (Num b, Checked a b) => Num (Check a b) where
(+) = lift2c (+) (-) = lift2c (-) (*) = lift2c (*) negate = liftc negate abs = liftc abs signum = liftc signum fromInteger = check . fromInteger
instance (Real b, Checked a b) => Real (Check a b) where
toRational = lift toRational
instance (Integral b, Checked a b) => Integral (Check a b) where
quot = lift2c quot rem = lift2c rem div = lift2c div mod = lift2c mod quotRem = lift2p quotRem divMod = lift2p divMod toInteger = lift toInteger</lang>
Now we can declare the a subrange 1..10 of integer like this:
<lang haskell>newtype TinyInt = TinyInt Int
instance Checked TinyInt Int where
check x | x >= 0 && x <= 10 = Check x | otherwise = error "Out of range"</lang>
In the same way, we could now declare the subtype of the even integers: <lang haskell>newtype EvenInt = EvenInt Int
instance Checked EvenInt Int where check x | even x = Check x | otherwise = error "Not even"</lang>
Similarly, we could declare the subtype of floating point numbers with restricted exponent, and so on.
Java
The closest you can get to defining a primitive type in Java is making a new wrapper class with methods for math operations.
This example class throws an exception if the value is out of the bounds; it is implemented only in the assignment method "assign" and the addition method "add". The class can be easily extended.
<lang java>class BoundedIntOutOfBoundsException extends Exception {
public BoundedIntOutOfBoundsException(int v, int l, int u) { super("value " + v + " is out of bounds [" + l + "," + u + "]"); }
}
class BoundedInt {
private int value; private int lower; private int upper;
public BoundedInt(int l, int u) { lower = Math.min(l, u); upper = Math.max(l, u); }
private boolean checkBounds(int v) { return (v >= this.lower) && (v <= this.upper); }
public void assign(BoundedInt i) throws BoundedIntOutOfBoundsException {{ assign(i.value()); //could still throw Exception if the other BoundedInt has different bounds }
public void assign(int v) throws BoundedIntOutOfBoundsException { if ( checkBounds(v) ) { this.value = v; } else { throw new BoundedIntOutOfBoundsException(v, this.lower, this.upper); } }
public int add(BoundedInt i) throws BoundedIntOutOfBoundsException { return add(i.value()); }
public int add(int i) throws BoundedIntOutOfBoundsException { if ( checkBounds(this.value + i) ) { this.value += i; } else { throw new BoundedIntOutOfBoundsException(this.value + i, this.lower, this.upper); } return this.value; }
public int value() { return this.value; }
}
public class Bounded {
public static void main(String[] args) throws BoundedIntOutOfBoundsException { BoundedInt a = new BoundedInt(1, 10); BoundedInt b = new BoundedInt(1, 10);
a.assign(6); try { b.assign(12); } catch (Exception e) { System.out.println(e.getMessage()); } b.assign(9); try { a.add(b.value()); } catch (Exception e) { System.out.println(e.getMessage()); } }
}</lang>
JavaScript
<lang JavaScript>function Num(n){
n = Math.floor(n); if(isNaN(n)) throw new TypeError("Not a Number"); if(n < 1 || n > 10) throw new TypeError("Out of range"); this._value = n;
} Num.prototype.valueOf = function() { return this._value; } Num.prototype.toString = function () { return this._value.toString();}
var w = new Num(3), x = new Num(4);
WScript.Echo(w + x); //7 WScript.Echo(x - w); //1 WScript.Echo(w * x); //12 WScript.Echo(w / x); //0.75 WScript.Echo(w < x); //true WScript.Echo(x < w); //false
var y = new Num(0); //TypeError var z = new Num(11); //TypeError </lang>
jq
In the following, we define a new type named "smallint", with arithmetic operations that either return the "smallint" one would expect from ordinary arithmetic, or else return nothing (an empty stream), as opposed to returning null or raising an error. The choice of an empty stream is made to highlight this alternative.
By design, jq's types are constrained to be JSON types, so jq's type system cannot be used to create a new type, but we can create a parallel type system based on JSON objects. We shall do so using the convention that if an object has a key named "type", then the type of the object will be the given value:<lang jq>def typeof:
if type == "object" and has("type") then .type else type end;</lang>We also define a generic "pretty-print" filter:
<lang jq>def pp:
if type == "object" and has("type") then "\(.type)::\(.value)" else . end;</lang>Our instances of smallint will look like this: {"type": "smallint", "value": 0}
This definition ensures that jq's basic tests for equality and inequality (== and !=) can be used for the instances of smallint.
In fact the builtin comparison functions (< and >) will also have the correct semantics, as will sort and unique.
As of this writing, the official release of jq does not support modules, so we will not use jq's new module system here, but it would allow us to place all the smallint functions in a Smallint module.
To generate instances of smallint, we define a function, smallint(i), that will return an instance corresponding to an integer, i, if it is in range. As noted above, it will otherwise return nothing at all (as opposed to null or raising an error):<lang jq>def smallint(i): i as $i
| if (i|type) == "number" and i == (i|floor) and i > 0 and i < 11 then {"type": "smallint", "value": i} else empty end ;
- A convenience function to save typing:
def s(i): smallint(i);
- To convert from the pretty-print representation back to smallint:
def tosmallint:
if type == "string" and startswith("smallint::") then split("::") | smallint( .[1] | tonumber ) else empty end ;</lang>That leaves just basic arithmetic operations: add minus times mod div <lang jq>def add(a;b): smallint(a.value + b.value);
def minus(a;b): smallint(a.value - b.value); def times(a;b): smallint(a.value * b.value); def mod(a;b): smallint(a.value % b.value); def divide(a;b): smallint( (a.value / b.value) | floor );</lang>Examples:<lang jq>s(1) < s(3) # => true add( s(1); s(2)) | pp # "smallint::3" add( s(6); s(6)) # (nothing)</lang>
Lasso
<lang Lasso>define dint => type {
data private value public oncreate(value::integer) => {
fail_if(#value < 1,#value+' less than 1 ') fail_if(#value > 10,#value+' greater than 10') .value = #value
}
public +(rhs::integer) => dint(.value + #rhs) public -(rhs::integer) => dint(.value - #rhs) public *(rhs::integer) => dint(.value * #rhs) public /(rhs::integer) => dint(.value / #rhs) public %(rhs::integer) => dint(.value % #rhs)
public asstring() => string(.value)
}
dint(1) // 1 dint(10) // 10
dint(0) // Error: 0 less than 1 dint(2) - 5 // Error: -3 less than 1
dint(11) // Error: 11 greater than 10 dint(10) + 1 // Error: 11 greater than 10 dint(10) * 2 // Error: 20 greater than 10 </lang>
MATLAB
In a weird way MATLAB has no primitive data types. All data types are defined as a MATLAB class somewhere in the MATLAB install directory. Therefore, to define a primitive data type, you define a class. Below is a class called "RingInt" that has the properties of an integer data type, but the values are restricted in the range [1,10]. Include in the definition are functions that overload some of the built-in MATLAB operators, e.g. addition and subtraction.
In a folder named "@RingInt" RingInt.m: <lang MATLAB>classdef RingInt
properties value end methods %RingInt constructor function theInt = RingInt(varargin) if numel(varargin) == 0 theInt.value = 1; elseif numel(varargin) > 1 error 'The RingInt constructor can't take more than 1 argument.'; else %Makes sure any doubles are coerced to ints if not(isinteger(varargin{1})) varargin{1} = int32(varargin{1}); end %Maps out of bound values to the proper range if varargin{1} > 10 theInt.value = varargin{1} - (10 * (idivide(varargin{1},10,'ceil') - 1)); elseif varargin{1} < 1 theInt.value = varargin{1} + (10 * (idivide(abs(varargin{1}),10,'floor') + 1)); else theInt.value = varargin{1}; end end end %constructor %Overload the "+" operator function sum = plus(firstNumber,secondNumber) if isa(firstNumber,'RingInt') && isa(secondNumber,'RingInt') sum = firstNumber.value + secondNumber.value; elseif isa(firstNumber,'RingInt') && not(isa(secondNumber,'RingInt')) sum = firstNumber.value + secondNumber; else sum = secondNumber.value + firstNumber; end sum = RingInt(sum); end %+ %Overload the "-" operator function difference = minus(firstNumber,secondNumber) if isa(firstNumber,'RingInt') && isa(secondNumber,'RingInt') difference = firstNumber.value - secondNumber.value; elseif isa(firstNumber,'RingInt') && not(isa(secondNumber,'RingInt')) difference = firstNumber.value - secondNumber; else difference = firstNumber - secondNumber.value; end difference = RingInt(difference); end %- %Overload the "==" operator function trueFalse = eq(firstNumber,secondNumber) if isa(firstNumber,'RingInt') && isa(secondNumber,'RingInt') trueFalse = firstNumber.value == secondNumber.value; else error 'You can only compare a RingInt to another RingInt'; end
end %== %Overload the display() function function display(ringInt) disp(ringInt); end %Overload the disp() function function disp(ringInt) disp(sprintf('\nans =\n\n\t %d\n',ringInt.value)); end end %methods
end %classdef
</lang>
Sample Usage: <lang MATLAB>>> RingInt(10) + 1
ans =
1
>> RingInt(5) - 20
ans =
5
>> a = RingInt(3)
ans =
3
>> a == RingInt(3)
ans =
1</lang>
Modula-3
In Modula-3, subrange types are automatically subtypes of their base type, so if you define a type called MyInt
to be the subrange [1..10] then MyInt
is a subtype of INTEGER
. If we defined MyChar
as the subrange ['A'..'C'] then MyChar
would be a subtype of CHAR
.
<lang modula3>TYPE MyInt = [1..10];</lang>
MyInt
can now be used anywhere an INTEGER
can, such as the standard arithmetic functions. Trying to assign a value outside of the range of MyInt
will result in a compile time warning, and/or a runtime error.
Nim
Subranges are supported by the language and automatically boundchecked: <lang nim>var x: range[1..10] = 10
x = 5
x = x + 6 # Runtime error: Value out of range
x = 12 # Compile error: Invalid conversion</lang>
OCaml
<lang ocaml>exception Out_of_bounds
type 'a bounds = { min: 'a; max: 'a }
type 'a bounded = { value: 'a; bounds: 'a bounds }
let mk_bounds ~min ~max = { min=min; max=max } ;; (** val mk_bounds : min:'a -> max:'a -> 'a bounds *)
let check_bounds ~value ~bounds =
if value < bounds.min || value > bounds.max then raise Out_of_bounds ;;
(** val check_bounds : value:'a -> bounds:'a bounds -> unit *)
let mk_bounded ~value ~bounds =
check_bounds ~value ~bounds; { value=value; bounds=bounds } ;;
(** val mk_bounded : value:'a -> bounds:'a bounds -> 'a bounded *)
let op f a b =
let res = f a.value b.value in if a.bounds <> b.bounds then invalid_arg "different bounds"; check_bounds res a.bounds; (mk_bounded res a.bounds) ;;
(** val op : ('a -> 'a -> 'a) -> 'a bounded -> 'a bounded -> 'a bounded *)</lang>
Using in the interactive top level: <lang ocaml># let range = mk_bounds 1 10 ;; val range : int bounds = {min = 1; max = 10}
- let a = mk_bounded 2 range ;;
val a : int bounded = {value = 2; bounds = {min = 1; max = 10}}
- let b = mk_bounded 5 range ;;
val b : int bounded = {value = 5; bounds = {min = 1; max = 10}}
- let c = mk_bounded 14 range ;;
Exception: Out_of_bounds.
- op ( + ) a b ;;
- : int bounded = {value = 7; bounds = {min = 1; max = 10}}</lang>
which can be used with floats in the same way: <lang ocaml># let rf = mk_bounds 1.0 10.0 ;; val rf : float bounds = {min = 1.; max = 10.}
- let a = mk_bounded 2.2 rf
and b = mk_bounded 5.6 rf in op ( +. ) a b ;;
- : float bounded = {value = 7.8; bounds = {min = 1.; max = 10.}}</lang>
ooRexx
<lang oorexx>/* REXX ----------------------------------------------------------------
- 21.06.2014 Walter Pachl
- implements a data type tinyint that can have an integer value 1..10
- 22.06.2014 WP corrected by Rony Flatscher to handle arithmetic
- ---------------------------------------------------------------------*/
a=.tinyint~new(1) ; Say 'a='||a~value Say '2*a='||(2*a) Say 'a*2='||((0+a)*2) say "---> rony was here: :)" say "The above statement was in effect: '(0+a)*2', NOT 'a*2*, hence it worked!" say "These statements work now:" say "(a)*2:" (a)*2 say "a*2: " a*2 say "<--- rony was here, The end. :)" b=.tinyint~new(11); Say 'b='||b~value b=.tinyint~new('B'); Say 'b='||b~value say 'b='||(b) -- show string value Say '2*b='||(2*b)
- class tinyint
- method init
Expose v Use Arg i Select When datatype(i,'W') Then Do If i>=1 & i<=10 Then v=i Else Do Say 'Argument 1 must be between 1 and 10' Raise Syntax 88.907 array(1,1,10,i) End End Otherwise Do Say 'Argument 1 must be a whole number between 1 and 10' Raise Syntax 88.905 array(1,i) End End
- method string
Expose v Return v
- method value
Expose v Return v
-- rgf, 20140622, intercept unknown messages, forward arithmetic messages to string value
- method unknown
expose v use arg methName, methArgs if wordpos(methName, "+ - * / % //")>0 then -- an arithmetic message in hand? forward message (methName) to (v) array (methArgs[1])
</lang> output
a=1 2*a=2 a*2=2 ---> rony was here: :) The above statement was in effect: '(0+a)*2', NOT 'a*2*, hence it worked! These statements work now: (a)*2: 2 a*2: 2 <--- rony was here, The end. :) Argument 1 must be between 1 and 10 28 *-* Raise Syntax 88.907 array(1,1,10,i) *-* Compiled method NEW with scope "Object" 14 *-* b=.tinyint~new(11); Error 88 running D:\tinyint2.rex line 28: Invalid argument Error 88.907: Argument 1 must be in the range 1 to 10; found "11"
Oz
Normally new data types are implemented as classes or as abstract data types in modules. We cannot extend operators, though.
In this case we are lucky. As a feature of constraint programming we can easily constrain the domain of integers.
<lang oz>declare
I
in
I::1#10 I = {Pow 2 4}</lang>
Output:
%***************************** failure ************************** %** %** Tell: I{1#10} = 16 %** %** Call Stack: %** procedure 'IntPow' in file "/Users/raph/devel/mozdss-branch/mozart/share/lib/base/Number.oz", line 32, column 3, PC = 16461488 %**--------------------------------------------------------------
Pascal
The details of range checks are compiler specific. With Freepascal range checks can also be enabled on the command line with the -Cr option. <lang pascal>Program BoundInteger(output);
{$RANGECHECKS ON}
type
TPartialInteger = 1..10;
var
testvar: TPartialInteger; i: integer;
begin
for i := 1 to 11 do begin writeln(i); testvar := i; end;
end.</lang> Output:
% ./BoundInteger 1 2 3 4 5 6 7 8 9 10 11 Runtime error 201 at $000113A6 $000113A6 $0002F586 $00011309 $00011238 $00000001
Perl
<lang perl>package One_To_Ten; use Carp qw(croak); use Tie::Scalar qw(); use base qw(Tie::StdScalar);
sub STORE {
my $self = shift; my $val = int shift; croak 'out of bounds' if $val < 1 or $val > 10; $$self = $val;
};
package main; tie my $t, 'One_To_Ten'; $t = 3; # ok $t = 5.2; # ok, silently coerced to int $t = -2; # dies, too small $t = 11; # dies, too big $t = 'xyzzy';
- dies, too small. string is 0 interpreted numerically</lang>
Perl 6
<lang perl6>subset OneToTen of Int where 1..10
my OneToTen $n = 5; $n += 6;</lang>
Here the result 11 fails to smartmatch against the range 1..10
, so the second assignment throws an exception. You may use any valid smartmatch predicate within a where
clause, so the following one-argument closure is also legal:
<lang perl6>subset Prime of Int where -> $n { $n > 1 and so $n %% none 2 .. $n.sqrt }</lang>
PicoLisp
<lang PicoLisp>(class +BoundedInt)
- value lower upper
(dm T (Low Up)
(=: lower (min Low Up)) (=: upper (max Low Up)) )
(de "checkBounds" (Val)
(if (>= (: upper) Val (: lower)) Val (throw 'boundedIntOutOfBounds (pack "value " Val " is out of bounds [" (: lower) "," (: upper) "]" ) ) ) )
(dm set> (Val)
(=: value ("checkBounds" Val)) )
(dm +> (Val)
(=: value ("checkBounds" (+ Val (: value)))) )
(dm val> ()
(: value) )
(de main ()
(let (A (new '(+BoundedInt) 1 10) B (new '(+BoundedInt) 1 10)) (set> A 6) (when (catch 'boundedIntOutOfBounds (set> B 12) NIL) (prinl @) ) (set> B 9) (when (catch 'boundedIntOutOfBounds (+> A (val> B)) NIL) (prinl @) ) ) )</lang>
Output:
: (main) value 12 is out of bounds [1,10] value 15 is out of bounds [1,10]
Python
This doesn't really apply as Python names don't have a type, but something can be done: <lang python>>>> class num(int):
def __init__(self, b): if 1 <= b <= 10: return int.__init__(self+0) else: raise ValueError,"Value %s should be >=0 and <= 10" % b
>>> x = num(3)
>>> x = num(11)
Traceback (most recent call last):
File "<pyshell#394>", line 1, in <module> x = num(11) File "<pyshell#392>", line 6, in __init__ raise ValueError,"Value %s should be >=0 and <= 10" % b
ValueError: Value 11 should be >=0 and <= 10 >>> x 3 >>> type(x) <class '__main__.num'> >>></lang>
Racket
Most Racket programmers will use contracts to enforce additional guarantees on values. In the following example, the program exports x with a contract that ensures it is a number between 1 and 10.
<lang racket>
- lang racket
(provide (contract-out [x 1-to-10/c]))
(define 1-to-10/c (between/c 1 10))
(define x 5) </lang>
In Typed Racket, it is possible to define a type that only contains the integers from 1 to 10. However, this type is inconvenient to use and is unlikely to be used in practice.
<lang racket>
- lang typed/racket
(define-type 1UpTo10 (U 1 2 3 4 5 6 7 8 9 10))
- type-checks
(: x 1UpTo10) (define x 3)
- does not type-check
(: y 1UpTo10) (define y 18) </lang>
Retro
<lang Retro>{{
variable update
---reveal---
: .limited @update &! &@ if update off ; : to dup 1 10 within [ update on ] [ drop "Out of bounds\n" puts ] if ; : limited: create 1 , &.limited reclass ;
}}</lang>
This creates a data element that returns a value from 1 to 10. Alteration of the value is possible using to.
<lang Retro>limited: foo 1 to foo foo .s 51 to foo foo .s bye</lang>
Ruby
ref http://codeidol.com/other/rubyckbk/Numbers/Simulating-a-Subclass-of-Fixnum/
Some object-oriented languages won't let you subclass the "basic" data types like integers. Other languages implement those data types as classes, so you can subclass them, no questions asked. Ruby implements numbers as classes (Integer, with its concrete subclasses Fixnum and Bignum), and you can subclass those classes. If you try, though, you'll quickly discover that your subclasses are useless: they don't have constructors. Ruby jealously guards the creation of new Integer objects. This way it ensures that, for instance, there can be only one Fixnum instance for a given number The easiest way to delegate all methods is to create a class that's nearly empty and define a method_missing method.
<lang ruby>require 'test/unit' include Test::Unit::Assertions
class MyInt
@@min = 1 @@max = 10 attr_reader :value private :value def initialize(val) begin v = Integer(val) rescue ArgumentError raise ArgumentError, "invalid value '#{val}', must be an integer" end unless v.between?(@@min, @@max) raise ArgumentError, "invalid value '#{v}', must be between #{@@min} and #{@@max}" end @value = v end def method_missing(m, *args) super unless @value.respond_to?(m) myint_args = args.collect do |arg| arg.kind_of?(self.class) ? arg.to_int : arg end result = @value.send(m, *myint_args) return result if m == :coerce case result when Integer MyInt.new(result) when Array result.collect do |element| element.kind_of?(Integer) ? MyInt.new(element) : element end else result end end def respond_to?(method) super or @value.respond_to? method end def to_int @value end def to_f Float(@value) end def to_s @value.to_s end def inspect to_s end
end
assert_raise(ArgumentError) { MyInt.new("foo") } # => invalid value 'foo', must be an integer
assert_raise(ArgumentError) { MyInt.new(11) } # => invalid value '11', must be an integer
a = MyInt.new(7) b = MyInt.new(5)
c = 5 + a assert_kind_of(Fixnum, c) assert_equal(12, c)
c = a + 2 assert_kind_of(MyInt, c) assert_equal(9, c.to_int)
c = a + 2.8 assert_kind_of(Float, c) assert_equal(9.8, c)
c = a - b assert_kind_of(MyInt, c) assert_equal(2, c.to_int)
assert_raise(ArgumentError) { c = a + b } # => invalid value '12', must be an integer assert_raise(ArgumentError) { c = b - a } # => invalid value '-2', must be an integer</lang>
Scala
<lang Scala> class TinyInt(val int: Byte) {
import TinyInt._ require(int >= lower && int <= upper, "TinyInt out of bounds.") override def toString = int.toString }
object TinyInt { val (lower, upper) = (1, 10)
def apply(i: Byte) = new TinyInt(i) }
val test = (TinyInt.lower to TinyInt.upper).map(n => TinyInt(n.toByte))</lang>
Sidef
<lang ruby>class MyInt(value) {
def min = 1; def max = 10;
method new(value is Number) { (value > max) || (value < min) && ( die "Invalid value '#{value}': must be between #{min} and #{max}"; ); !value.is_int && ( die "Invalid value '#{value}': must be an integer"; ); }
method new(value) { die "Invalid value '#{value}'; expected a number"; }
method get_value { value.get_value.get_value; }
method AUTOLOAD(name, *args) { var result = value.(name)(args...);
result.is_a(Number) && return MyInt(result.int);
result; }
}
-
- Tests:
var a = MyInt(2); # creates a new object of type `MyInt` a += 7; # adds 7 to a say a; # => 9 say a/2; # => 4
var b = (a - 3); # b is of type `MyInt` say b; # => 6
say a.to_hex; # => "0x9" -- an hexadecimal string
a -= 7; # a=2 say (a + b); # => 8 -- the result of (2 + 6)
a += 4; # a=6 say a+b; # error: Invalid value '12'; must be between 1 and 10</lang>
Tcl
Tcl does not attach types to values or variables, but it does allow the programmer to create traces on variables that can be used to enforce type-like constraints among other things. Traces are procedures that execute when variables are read, written and/or unset. (Traces are also available for commands and for the execution of a script.) Tcl's compiler does not enforce these constraints; they're strictly runtime entities. <lang tcl>namespace eval ::myIntType {
variable value_cache array set value_cache {} variable type integer variable min 1 variable max 10 variable errMsg "cannot set %s to %s: must be a $type between $min and $max"
} proc ::myIntType::declare varname {
set ns [namespace current] uplevel [list trace add variable $varname write ${ns}::write] uplevel [list trace add variable $varname unset ${ns}::unset_var]
} proc ::myIntType::unset_var {varname args} {
variable value_cache unset value_cache($varname)
} proc ::myIntType::validate {value} {
variable type variable min variable max expr {[string is $type -strict $value] && $min <= $value && $value <= $max}
} proc ::myIntType::write {varname args} {
variable value_cache upvar $varname var set value $var if {[validate $value]} { set value_cache($varname) $value } else { if {[info exists value_cache($varname)]} { set var $value_cache($varname) } variable errMsg error [format $errMsg $varname $value] }
}</lang>
So, in an interactive tclsh we can see:
% myIntType::declare foo % set foo ;# regular Tcl error: foo is declared but still unset can't read "foo": no such variable % set foo bar can't set "foo": cannot set foo to bar: must be a integer between 1 and 10 % set foo 3 3 % incr foo 10 can't set "foo": cannot set foo to 13: must be a integer between 1 and 10 % incr foo -10 can't set "foo": cannot set foo to -7: must be a integer between 1 and 10 % set foo 0 can't set "foo": cannot set foo to 0: must be a integer between 1 and 10 % set foo 3 % set foo [expr {$foo * 1.5}] can't set "foo": cannot set foo to 4.5: must be a integer between 1 and 10 % set foo 3 % unset foo %
Toka
<lang toka>needs quotes {
variable update [ update @ [ ! ] [ @ ] ifTrueFalse update off ] is action [ dup >r 0 11 r> within [ update on ] [ drop ." Out of bounds\n " ] ifTrueFalse ] [ ` [ invoke cell-size malloc # ` action compile ` ] invoke is ]
} is value:1-10:
is to
value:1-10: foo 1 to foo foo .</lang>
UNIX Shell
ksh93 has
- compound variables and
- "discipline functions" which get fired at get/set/unset events
<lang bash>typeset -i boundedint function boundedint.set {
nameref var=${.sh.name} if (( 1 <= .sh.value && .sh.value <= 10 )); then # stash the valid value as a backup, in case we need to restore it typeset -i var.previous_value=${.sh.value} else print -u2 "value out of bounds" # restore previous value .sh.value=${var.previous_value} fi
}
boundedint=-5; echo $boundedint boundedint=5; echo $boundedint boundedint=15; echo $boundedint</lang>
- Output:
value out of bounds 0 5 value out of bounds 5
Ursala
We define a new record type my_number
, having two fields, of which
one is the number and the other is the pair of bounds. Fields in
records can have initializing functions, and any function can throw an
exception, so we make the initializing function of the number field
that which throws an exception if it's outside the bounds. The
initializing function of the bounds field sets its default values to 1
and 10. The initializing functions are automatically evaluated any
time a record of type _my_number
is constructed by a function of the
form my_number$[...]
, so we can proceed to define all the arithmetic
operations we need as functions of this form in terms of the
corresponding operations on natural numbers with no further bounds
checking required.
<lang Ursala>#import nat
my_number ::
the_number %n -|~bounds.&BZ,~&B+ nleq~~lrlXPrX@G+ ~/the_number bounds|-?(~the_number,<'out of bounds'>!%) bounds %nW ~bounds.&B?(~bounds,(1,10)!)
add = my_number$[the_number: sum+ ~the_number~~] mul = my_number$[the_number: product+ ~the_number~~]</lang> test program: <lang Ursala>#cast _my_number
total = add(my_number[the_number: 3],my_number[the_number: 4])</lang> output:
my_number[the_number: 7,bounds: (1,10)]
test program demonstrating bounds checking: <lang Ursala>#cast _my_number
total = mul(my_number[the_number: 3],my_number[the_number: 4])</lang> compile-time diagnostic output:
fun:prim.fun:3:12: out of bounds
Note that restricted types are not idiomatic in Ursala if all we really want is integer arithmetic with run-time bounds checking, which can be done more directly like this. <lang Ursala>#import nat
- library+
- import
^|A(~&,//+ nrange(1,10)?</~& <'out of bounds'>!%)* ~&n-=<'sum','difference','product','quotient','remainder'>*~ %QI nat</lang>
This code creates a new library of functions of natural numbers by selecting several of them by name from the standard nat
library and putting a wrapper around each one that checks the bounds on the result and throws an exception if necessary. These functions can be used as drop-in replacements for the standard ones.
Visual Basic
VB can't really do primitive data types, but they can be faked with classes.
TinyInt.cls: <lang vb>Private mvarValue As Integer
Public Property Let Value(ByVal vData As Integer)
If (vData > 10) Or (vData < 1) Then Error 380 'Invalid property value; could also use 6, Overflow Else mvarValue = vData End If
End Property
Public Property Get Value() As Integer
Value = mvarValue
End Property
Private Sub Class_Initialize()
'vb defaults to 0 for numbers; let's change that... mvarValue = 1
End Sub</lang>
Usage (in this case, from a form): <lang vb>Public x As TinyInt
Private Sub Form_Click()
'0-11, to give chance of errors; also not integers, because VB massages data to fit, if possible. x = Rnd * 12 Me.Print x
End Sub
Private Sub Form_Load()
Randomize Timer Set x = New TinyInt '"Set = New" REQUIRED
End Sub</lang>
Visual Basic .NET
Visual Basic .NET has full support for creating your own primitives, but every operator has to be implemented explicitly. Often developers will only implement the parts they are using and skip the rest.
Also note that some operators return a Double instead of a new LimitedInt. This was by choice in order to match the behavior of Integers in VB.
<lang vbnet>Structure LimitedInt
Implements IComparable(Of LimitedInt) Implements IEquatable(Of LimitedInt)
Private m_Value As Integer 'treat the default, 0 as being really 1
Public ReadOnly Property Value() As Integer Get Return If(m_Value = 0, 1, m_Value) End Get End Property
Public Sub New(ByVal value As Integer) If value < 1 Or value > 10 Then Throw New ArgumentOutOfRangeException("value") m_Value = value End Sub
Public Function CompareTo(ByVal other As LimitedInt) As Integer Implements System.IComparable(Of LimitedInt).CompareTo Return Me.Value - other.Value End Function Public Overloads Function Equals(ByVal other As LimitedInt) As Boolean Implements System.IEquatable(Of LimitedInt).Equals Return Me.Value = other.Value End Function
Public Overrides Function GetHashCode() As Integer Return Value.GetHashCode End Function
Public Overrides Function Equals(ByVal obj As Object) As Boolean If TypeOf obj Is LimitedInt Then Return CType(obj, LimitedInt) = Me End Function
Public Shared Operator =(ByVal left As LimitedInt, ByVal right As LimitedInt) As Boolean Return left.Equals(right) End Operator
Public Shared Operator <>(ByVal left As LimitedInt, ByVal right As LimitedInt) As Boolean Return Not (left = right) End Operator
Public Shared Operator +(ByVal left As LimitedInt, ByVal right As LimitedInt) As LimitedInt Dim temp As Integer = left.Value + right.Value Select Case temp Case 1 To 10 : Return New LimitedInt(temp) Case Else : Throw New OverflowException End Select End Operator
Public Shared Operator -(ByVal left As LimitedInt, ByVal right As LimitedInt) As LimitedInt Dim temp As Integer = left.Value - right.Value Select Case temp Case 1 To 10 : Return New LimitedInt(temp) Case Else : Throw New OverflowException End Select End Operator
Public Shared Operator *(ByVal left As LimitedInt, ByVal right As LimitedInt) As LimitedInt Dim temp As Integer = left.Value * right.Value Select Case temp Case 1 To 10 : Return New LimitedInt(temp) Case Else : Throw New OverflowException End Select End Operator
Public Shared Operator /(ByVal left As LimitedInt, ByVal right As LimitedInt) As Double Return left.Value / right.Value End Operator
Public Shared Operator \(ByVal left As LimitedInt, ByVal right As LimitedInt) As LimitedInt Dim temp As Integer = left.Value \ right.Value Select Case temp Case 1 To 10 : Return New LimitedInt(temp) Case Else : Throw New OverflowException End Select End Operator
Public Shared Operator Mod(ByVal left As LimitedInt, ByVal right As LimitedInt) As LimitedInt Dim temp As Integer = left.Value Mod right.Value Select Case temp Case 1 To 10 : Return New LimitedInt(temp) Case Else : Throw New OverflowException End Select End Operator
Public Shared Operator And(ByVal left As LimitedInt, ByVal right As LimitedInt) As LimitedInt Dim temp As Integer = left.Value And right.Value Select Case temp Case 1 To 10 : Return New LimitedInt(temp) Case Else : Throw New OverflowException End Select End Operator
Public Shared Operator Or(ByVal left As LimitedInt, ByVal right As LimitedInt) As LimitedInt Dim temp As Integer = left.Value Or right.Value Select Case temp Case 1 To 10 : Return New LimitedInt(temp) Case Else : Throw New OverflowException End Select End Operator
Public Shared Operator Xor(ByVal left As LimitedInt, ByVal right As LimitedInt) As LimitedInt Dim temp As Integer = left.Value Xor right.Value Select Case temp Case 1 To 10 : Return New LimitedInt(temp) Case Else : Throw New OverflowException End Select End Operator
Public Shared Operator ^(ByVal left As LimitedInt, ByVal right As LimitedInt) As Double Return left.Value ^ right.Value End Operator
Public Shared Operator <(ByVal left As LimitedInt, ByVal right As LimitedInt) As Boolean Return left.Value < right.Value End Operator
Public Shared Operator >(ByVal left As LimitedInt, ByVal right As LimitedInt) As Boolean Return left.Value > right.Value End Operator
Public Shared Operator <=(ByVal left As LimitedInt, ByVal right As LimitedInt) As Boolean Return left.Value <= right.Value End Operator
Public Shared Operator >=(ByVal left As LimitedInt, ByVal right As LimitedInt) As Boolean Return left.Value >= right.Value End Operator
Public Shared Widening Operator CType(ByVal left As LimitedInt) As Integer Return left.Value End Operator
Public Shared Narrowing Operator CType(ByVal left As Integer) As LimitedInt Return New LimitedInt(left) End Operator
End Structure</lang>
- Programming Tasks
- Basic language learning
- Type System
- Ada
- ALGOL 68
- C sharp
- C++
- Clojure
- Common Lisp
- D
- E
- Euphoria
- Fortran
- Haskell
- Java
- JavaScript
- Jq
- Lasso
- MATLAB
- Modula-3
- Nim
- OCaml
- OoRexx
- Oz
- Pascal
- Perl
- Perl 6
- PicoLisp
- Python
- Racket
- Retro
- Ruby
- Scala
- Sidef
- Tcl
- Toka
- UNIX Shell
- Ursala
- Visual Basic
- Visual Basic .NET
- AWK/Omit
- Axe/Omit
- BBC BASIC/Omit
- Bc/Omit
- C/Omit
- Dc/Omit
- Déjà Vu/Omit
- Erlang/Omit
- Go/Omit
- Inform 7/Omit
- J/Omit
- JavaScript/Omit
- Mathematica/Omit
- Maxima/Omit
- PARI/GP/Omit
- TI-83 BASIC/Omit
- TI-89 BASIC/Omit
- Vim Script/Omit