May someone highlight to me why I am getting the syntax errors for the main function, so that I can fix it. I am quite new to the language. Actually I was introduced to it through the assignment, so I am totally lost as to how to refactor it to avoid the syntax error:
val IDs = [410021001,410021002,410021003,410021004,410021005,410021006,410021007,410021008,410021009,410021010];
val Names = ["Alan","Bob","Carrie","David","Ethan","Frank","Gary","Helen","Igor","Jeff"]: string list;
val HW1 = [90.0,85.0,90.0,117.0,85.0,90.0,117.0,117.0,117.0,117.0] : real list;
val HW2 = [84.5,49.0,110.5,85.0,56.0,65.0,65.0,59.5,50.0,50.0] : real list;
val HW3 = [117.0,117.0,117.0,0.0,65.0,117.0,50.0,51.0,75.0,75.0] : real list;
val Midterm = [60.0,57.0,6.0,44.0,72.0,43.0,54.0,75.0,53.0,75.0] : real list;
val Final = [66.0,64.0,62.0,55.0,66.0,75.0,75.0,75.0,75.0,75.0] : real list;
fun score(HW1, HW2, HW3, Midterm, Final) =
round(HW1 * 0.1 + HW2 * 0.1 + HW3 * 0.1 + Midterm * 0.3 + Final * 0.4);
fun letterGrade(score) =
if score >= 90 then "A+"
else if score >= 85 then "A"
else if score >= 80 then "A-"
else if score >= 77 then "B+"
else if score >= 73 then "B"
else if score >= 70 then "B-"
else if score >= 67 then "C+"
else if score >= 63 then "C"
else if score >= 60 then "C-"
else if score >= 50 then "D"
else "E";
val i = 0
val max = length(IDs)
fun main() =
while i < max do
var ind_score = score(HW1[i], HW2[i], HW3[i], Midterm[i], Final[i])
var grade = letterGrade(ind_score)
print(IDs[i], " ", Names[i], " ", ind_score, " ", grade)
i = i + 1
end
end
This is the error I am producing after running my programme, which shows that my errors start at this function:
Terminal feedback
Part 1 - Straightforward corrections
I'll go from the simplest to the most complex. I'll also provide a more functional implementation in the end, without the while loop.
The construct var does not exist in ML. You probably meant val ind_score = ...
Array indexing is not done by array[i]. You need (as with everything else) a function to do that. The function happens to be List.nth. So, everywhere you have HW1[i], you should have List.nth(HW1, i).
Most language constructs expect a single expression, so you usually cannot simply string commands as we do in imperative languages. Thus, there are some constructs missing after the do in your while.
Variables in functional languages are usually immutable by default, so you have to indicate when you want something to be mutable. In your while, you want i to be mutable, so it has to be declared and used as such: val i = ref 0. When using the value, you have to use the syntax !i to get the 'current' value of the variable (essentially, de-referencing it).
The function call syntax in ML does not use (). When you call a function like score(a, b, c, d) what you are doing is creating a tuple (a, b, c, d) and passing it as a single argument to the function score. This is an important distinction because you are actually passing a tuple to your print function, which does not work because print expects a single argument of type string. By the way, the string concatenation operator is ^.
If you do all these changes, you'll get to the following definition of main. It is quite ugly but we will fix that soon:
val i = ref 0 (* Note that i's type is "int ref". Think about it as a pointer to an integer *)
val max = length(IDs)
fun main() =
while !i < max do (* Notice how annoying it is to always de-reference i and that the syntax to access a list element is not very convenient *)
let (* The 'let in end' block is the way to define values that will be used later *)
val ind_score = score(List.nth(HW1, !i), List.nth(HW2, !i), List.nth(HW3, !i), List.nth(Midterm, !i), List.nth(Final, !i))
val grade = letterGrade(ind_score)
in ( (* The parenthesis here allow stringing together a series of "imperative" operations *)
print(Int.toString(List.nth(IDs, !i)) ^ " " ^ List.nth(Names, !i) ^ " " ^ Int.toString(ind_score) ^ " " ^ grade ^ "\n");
i := !i + 1 (* Note the syntax := to re-define the value of i *)
)
end;
Part 2 - Making it more functional
Functional language programs are typically structured differently from imperative programs. A lot of small functions, pattern matching and recursion are typical. The code below is an example of how you could improve your main function (it is by no means "optimal" in terms of style though). A clear advantage of this implementation is that you do not even need to worry about the length of the lists. All you need to know is what to do when they are empty and when they are not.
(* First, define how to print the information of a single student.
Note that the function has several arguments, not a single argument that is a tuple *)
fun printStudent id name hw1 hw2 hw3 midterm final =
let
val ind_score = score (hw1, hw2, hw3, midterm, final)
val grade = letterGrade ind_score
in
print(Int.toString(id) ^ " " ^ name ^ " " ^ Int.toString(ind_score) ^ " " ^ grade ^ "\n")
end;
(* This uses pattern matching to disassemble the lists and print each element in order.
The first line matches an empty list on the first element (the others don't matter) and return (). Think of () as None in Python.
The second line disassemble each list in the first element and the rest of the list (first::rest), print the info about the student and recurse on the rest of the list.
*)
fun printAllStudents (nil, _, _, _, _, _, _) = ()
| printAllStudents (id::ids, name::names, hw1::hw1s, hw2::hw2s, hw3::hw3s, mid::midterms, final::finals) =
(printStudent id name hw1 hw2 hw3 mid final;
printAllStudents(ids, names, hw1s, hw2s, hw3s, midterms, finals));
printAllStudents(IDs, Names, HW1, HW2, HW3, Midterm, Final);
Note that it is a bit of a stretch to say that this implementation is more legible than the first one, even though it is slightly more generic. There is a way of improving it significantly though.
Part 3 - Using records
You may have noticed that there is a lot of repetition on the code above because we keep having to pass several lists and arguments. Also, if a new homework or test was added, several functions would have to be reworked. A way to avoid this is to use records, which work similarly to structs in C. The code below is a refactoring of the original code using a Student record. Note that, even though it has a slightly larger number of lines than your original code, it is (arguably) easier to understand and easier to update, if needed. The important part about records is that to access a field named field, you use an accessor function called #field:
(* Create a record type representing a student *)
type Student = {id:int, name:string, hw1:real, hw2:real, hw3:real, midterm:real, final:real};
(* Convenience function to construct a list of records from the individual lists of values *)
fun makeListStudents (nil, _, _, _, _, _, _) = nil (* if the input is empty, finish the list *)
| makeListStudents (id::ids, name::names, hw1::hw1s, hw2::hw2s, hw3::hw3s, mid::midterms, final::finals) = (* otherwise, add one record to the list and recurse *)
{id=id, name=name, hw1=hw1, hw2=hw2, hw3=hw3, midterm=mid, final=final} :: makeListStudents(ids, names, hw1s, hw2s, hw3s, midterms, finals);
val students = makeListStudents (IDs, Names, HW1, HW2, HW3, Midterm, Final);
fun score ({hw1, hw2, hw3, midterm, final, ...}: Student): int = (* Note the special patter matching syntax *)
round(hw1 * 0.1 + hw2 * 0.1 + hw3 * 0.1 + midterm * 0.3 + final * 0.4);
fun letterGrade (score) =
if score >= 90 then "A+"
else if score >= 85 then "A"
else if score >= 80 then "A-"
else if score >= 77 then "B+"
else if score >= 73 then "B"
else if score >= 70 then "B-"
else if score >= 67 then "C+"
else if score >= 63 then "C"
else if score >= 60 then "C-"
else if score >= 50 then "D"
else "E";
(* Note how this function became more legible *)
fun printStudent (st: Student) =
let
val ind_score = score(st)
val grade = letterGrade(ind_score)
in
print(Int.toString(#id(st)) ^ " " ^ #name(st) ^ " " ^ Int.toString(ind_score) ^ " " ^ grade ^ "\n")
end;
(* Note how, now that we have everything in a single list, we can use map *)
fun printAllStudents (students) = map printStudent students;
printAllStudents(students);
I am trying to figure out how to code quantifiers in CVC4, using the C++ interface. Here is an example I am trying to get to run, but cannot.
int main() {
SmtEngine smt(&em);
smt.setLogic("AUFLIRA"); // Set the logic
Expr three = em.mkConst(Rational(3));
Expr four = em.mkConst(Rational(4));
// make the list of bound variables in CVC4
Expr bound_var = em.mkBoundVar("x_bound", em.integerType());
vector<Expr> bound_vars;
bound_vars.push_back(bound_var);
Expr bound_var_list = em.mkExpr(kind::BOUND_VAR_LIST, bound_vars);
Expr declare = em.mkExpr(kind::EQUAL, bound_var, three); //x_bound =3
Expr check = em.mkExpr(kind::EQUAL, bound_var, four); //x_bound=4
//forall x_bound, x_bound=3, the constraint I want to declare as true
Expr expr = em.mkExpr(kind::FORALL, bound_var_list, declare);
smt.assertFormula(expr);
smt.push();
// expect to be INVALID
// I want to check that given forall x_bound, x_bound = 3
// then I ask CVC4: is it true that x_bound=4, or is it false?
std::cout << "Quantifier " << smt.query(check) << std::endl;
return 0;
}
Instead, I just get an error message:
Bound variables test
Quantifier unknown (INCOMPLETE)
But I define the quantifier as forall. What did I do wrong?
EDIT (asked https://www.andrew.cmu.edu/user/liminjia/):
Syntax is wrong. We want to know if
(forall x, x=3) IMPLIES (forall x, x=4)
is true or not. But CVC4 does not if the above formula is valid or not, because SMT solvers are not full-fledged first-order logic theorem provers.
If you want to try something that works, try in CVC4 language:
QUERY (FORALL (x:INT): x =4);
And in C++, we have
// check that forall x, x=3 is INVALID
void cvc4BoundVar() {
std::cout << "Bound variables test" << std::endl;
SmtEngine smt(&em);
smt.setLogic("AUFLIRA"); // Set the logic
Expr three = em.mkConst(Rational(3));
Expr v_expr = em.mkBoundVar("x_bound", em.integerType());
vector<Expr> bound_vars;
bound_vars.push_back(v_expr);
Expr bound_var_list = em.mkExpr(kind::BOUND_VAR_LIST, bound_vars);
Expr declare = em.mkExpr(kind::EQUAL, v_expr, three); //x=3
Expr check = em.mkExpr(kind::EQUAL, v_expr, three);
Expr expr = em.mkExpr(kind::FORALL, bound_var_list, declare); //forall x, x=3
std::cout << "Quantifier " << smt.query(expr) << std::endl;
}
I've wrote the following spec
"An IP4 address" should "belong to just one class" in {
val addrs = for {
a <- Gen.choose(0, 255)
b <- Gen.choose(0, 255)
c <- Gen.choose(0, 255)
d <- Gen.choose(0, 255)
} yield s"$a.$b.$c.$d"
forAll (addrs) { ip4s =>
var c: Int = 0
if (IP4_ClassA.unapply(ip4s).isDefined) c = c + 1
if (IP4_ClassB.unapply(ip4s).isDefined) c = c + 1
if (IP4_ClassC.unapply(ip4s).isDefined) c = c + 1
if (IP4_ClassD.unapply(ip4s).isDefined) c = c + 1
if (IP4_ClassE.unapply(ip4s).isDefined) c = c + 1
c should be (1)
}
}
That is very clear in its scope.
The test passes successfully but when I force it to fail (for example by commenting out one of the if statements) then ScalaCheck correctly reports the error but the message doesn't mention correctly the actual value used to evaluate the proposition. More specifically I get:
[info] An IP4 address
[info] - should belong to just one class *** FAILED ***
[info] TestFailedException was thrown during property evaluation.
[info] Message: 0 was not equal to 1
[info] Location: (NetSpec.scala:105)
[info] Occurred when passed generated values (
[info] arg0 = "" // 4 shrinks
[info] )
where you can see arg0 = "" // 4 shrinks doesn't show the value.
I've tried to add even a simple println statement to review the cases but the output appears to be trimmed. I get something like this
192.168.0.1
189.168.
189.
1
SOLUTION
import org.scalacheck.Prop.forAllNoShrink
import org.scalatest.prop.Checkers.check
"An IP4 address" should "belong to just one class" in {
val addrs = for {
a <- Gen.choose(0, 255)
b <- Gen.choose(0, 255)
c <- Gen.choose(0, 255)
d <- Gen.choose(0, 255)
} yield s"$a.$b.$c.$d"
check {
forAllNoShrink(addrs) { ip4s =>
var c: Int = 0
if (IP4.ClassA.unapply(ip4s).isDefined) c = c + 1
if (IP4.ClassB.unapply(ip4s).isDefined) c = c + 1
if (IP4.ClassC.unapply(ip4s).isDefined) c = c + 1
if (IP4.ClassD.unapply(ip4s).isDefined) c = c + 1
if (IP4.ClassE.unapply(ip4s).isDefined) c = c + 1
c == (1)
}
}
}
This is caused by ScalaCheck's test case simplification feature. ScalaCheck just sees that your generator produces a string value. Whenever it finds a value that makes your property false, it tries to simplify that value. In your case, it simplifies it four times until it ends up with an empty string, that still makes your property false.
So this is expected, although confusing, behavior. But you can improve the situation in three different ways.
You can select another data structure to represent your IP addresses. This will make ScalaCheck able to simplify your test cases in a more intelligent way. For example, use the following generator:
val addrs = Gen.listOfN(4, Gen.choose(0,255))
Now ScalaCheck knows that your generator only produces lists of length 4, and that it only contains numbers between 0 and 255. The test case simplification process will take this into account and not create any values that couldn't have been produced by the generator from start. You can do the conversion to string inside your property instead.
A second method is to add a filter directly to your generator, which tells ScalaCheck how an IP address string should look like. This filter is used during test case simplification. Define a function that checks for valid strings and attach it to your existing generator this way:
def validIP(ip: String): Boolean = ...
val validAddrs = addrs.suchThat(validIP)
forAll(validAddrs) { ... }
The third method is to simply disable the test case simplification feature altogether by using forAllNoShrink instead of forAll:
Prop.forAllNoShrink(addrs) { ... }
I should also mention that the two first methods require ScalaCheck version >= 1.11.0 to function properly.
UPDATE:
The listOfN list length is actually not respected by the shrinker any more, due to https://github.com/rickynils/scalacheck/issues/89. Hopefully this can be fixed in a future version of ScalaCheck.
I am working on ANLTR to support type checking. I am in trouble at some point. I will try to explain it with an example grammar, suppose that I have the following:
#members {
private java.util.HashMap<String, String> mapping = new java.util.HashMap<String, String>();
}
var_dec
: type_specifiers d=dec_list? SEMICOLON
{
mapping.put($d.ids.get(0).toString(), $type_specifiers.type_name);
System.out.println("identext = " + $d.ids.get(0).toString() + " - " + $type_specifiers.type_name);
};
type_specifiers returns [String type_name]
: 'int' { $type_name = "int";}
| 'float' {$type_name = "float"; }
;
dec_list returns [List ids]
: ( a += ID brackets*) (COMMA ( a += ID brackets* ) )*
{$ids = $a;}
;
brackets : LBRACKET (ICONST | ID) RBRACKET;
ID : ('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'0'..'9'|'_')*;
LBRACKET : '[';
RBRACKET : ']';
In rule dec_list, you will see that I am returning List with ids. However, in var_dec when I try to put the first element of the list (I am using only get(0) just to see the return value from dec_list rule, I can iterate it later, that's not my point) into mapping I get a whole string like
[#4,6:6='a',<17>,1:6]
for an input
int a, b;
What I am trying to do is to get text of each ID, in this case a and b in the list of index 0 and 1, respectively.
Does anyone have any idea?
The += operator creates a List of Tokens, not just the text these Tokens match. You'll need to initialize the List in the #init{...} block of the rule and add the inner-text of the tokens yourself.
Also, you don't need to do this:
type_specifiers returns [String type_name]
: 'int' { $type_name = "int";}
| ...
;
simply access type_specifiers's text attribute from the rule you use it in and remove the returns statement, like this:
var_dec
: t=type_specifiers ... {System.out.println($t.text);}
;
type_specifiers
: 'int'
| ...
;
Try something like this:
grammar T;
var_dec
: type dec_list? ';'
{
System.out.println("type = " + $type.text);
System.out.println("ids = " + $dec_list.ids);
}
;
type
: Int
| Float
;
dec_list returns [List ids]
#init{$ids = new ArrayList();}
: a=ID {$ids.add($a.text);} (',' b=ID {$ids.add($b.text);})*
;
Int : 'int';
Float : 'float';
ID : ('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'0'..'9'|'_')*;
Space : ' ' {skip();};
which will print the following to the console:
type = int
ids = [a, b, foo]
If you run the following class:
import org.antlr.runtime.*;
public class Main {
public static void main(String[] args) throws Exception {
TLexer lexer = new TLexer(new ANTLRStringStream("int a, b, foo;"));
TParser parser = new TParser(new CommonTokenStream(lexer));
parser.var_dec();
}
}