<div class="notebook"> <div class="nb-cell markdown" name="md1"> # Parsing quantities My students keep forgetting the units, so I want to give feedback to their numerical inputs. </div> <div class="nb-cell query" name="q1"> quantity("1.5 kg", N, Options) </div> <div class="nb-cell program" data-background="true" name="p1"> :- use_module(library(dcg/basics)). % % General numeric input like "1.5 cm" % quantity(String, Number, Options) :- ground(String), string_codes(String, Codes), quantity(Number, Options, Codes, []). match_quantities(Solution, Response, []) :- quantity(Solution, Number_sol, Options_sol), option(dec(Dec), Options_sol), quantity(Response, Number_res, Options_res), option(dec(Dec), Options_res), Number_sol = Number_res. match_quantities(Solution, Response, [buggy(dec(Response) \= Sol)]) :- quantity(Solution, Number_sol, Options_sol), option(dec(Sol), Options_sol), quantity(Response, Number_res, Options_res), option(dec(Res), Options_res), Sol \= Res, N is round(Number_sol * 10^min(Sol, Res)) / 10^min(Sol, Res), N is round(Number_res * 10^min(Sol, Res)) / 10^min(Sol, Res). % % 1.5E10 % quantity(Number, Options) --> number(Number, Options). % % 10 kg % quantity(Number, Options) --> number(Num, NumOpt), blanks, unit(_Unit, UnitOpt), { option(fac(Kilo), UnitOpt, 1), Number is Num * Kilo, option(dec(D), NumOpt, 0), Dec is D - log10(Kilo), merge_options([dec(Dec) | UnitOpt], NumOpt, Options) }. % Real number number(Number, Options) --> intdotfrac(Int, IntOpt), power(Pow), { Number is Int * 10^Pow, option(dec(D), IntOpt, 0), Dec is D - Pow, merge_options([dec(Dec)], IntOpt, Options) }. % sign(?Number, ?Options, ?Codes)// sign(+1, [sign(+)]) --> "+". sign(-1, [sign(-)]) --> "-". sign(-1, [sign(-)]) --> [226, 136, 146]. sign(1, [sign(none)]) --> []. % Natural number nat(Number) --> digits([H | T]), { number_codes(Number, [H | T]) }. dot --> ".". dot --> ",". % Fractional part frac(Number, [dec(Dec)]) --> digits([H | T]), { number_codes(Num, [H | T]), length([H | T], Dec), Number is Num / 10.0^Dec }. % 15 intdotfrac(Number, [dec(0) | Options]) --> sign(Sign, Options), nat(Int), { Number is Sign * Int }. % 15.5 intdotfrac(Number, Options) --> sign(Sign, SgnOpt), nat(Int), dot, frac(Frac, FracOpt), { Number is Sign * (Int + Frac), merge_options(SgnOpt, FracOpt, Options) }. % .70 intdotfrac(Number, Options) --> sign(Sign, SgnOpt), dot, frac(Frac, FracOpt), { Number is Sign * Frac, merge_options(SgnOpt, [int(empty) | FracOpt], Options) }. % times 10^E power(Pow) --> exp_e, sign(Sign, _), nat(P), { Pow is Sign * P }. power(0) --> []. exp_e --> "E". exp_e --> "e". exp_e --> blanks, "*", blanks, "10", blanks, "^", blanks. exp_e --> blanks, "*", blanks, "10", blanks, "**", blanks. exp_e --> blanks, [195, 151], blanks, "10", blanks, "^", blanks. exp_e --> blanks, [195, 151], blanks, "10", blanks, "**", blanks. unit('%', [fac(0.01), si('%')]) --> "%". unit(U, [si(SI) | Options]) --> modifier(M, Options), si(SI), { string_concat(M, SI, U) }. % Modifier modifier(k, [fac(1000)]) --> "k". modifier(c, [fac(0.01)]) --> "c". modifier(m, [fac(0.001)]) --> "m". modifier('', [fac(1)]) --> "". % Unit si(g) --> "g". si(m) --> "m". ex_quant :- S = ".5 kg", quantity(S, N, Options), writeln(S), writeln(N), writeln(Options). ex_quant :- S = "1.5 kg", quantity(S, N, Options), writeln(S), writeln(N), writeln(Options). ex_quant :- S = "15 kg", quantity(S, N, Options), writeln(S), writeln(N), writeln(Options). ex_quant :- S = "15 g", quantity(S, N, Options), writeln(S), writeln(N), writeln(Options). ex_quant :- S = "-15 g", quantity(S, N, Options), writeln(S), writeln(N), writeln(Options). ex_quant :- S = "-15E10 g", quantity(S, N, Options), writeln(S), writeln(N), writeln(Options). ex_quant :- S = "-1.5E-10 g", quantity(S, N, Options), writeln(S), writeln(N), writeln(Options). ex_quant :- S = "0.09", quantity(S, N, Options), writeln(S), writeln(N), writeln(Options). ex_quant :- S = "9%", quantity(S, N, Options), writeln(S), writeln(N), writeln(Options). ex_quant :- S = "9.1 %", quantity(S, N, Options), writeln(S), writeln(N), writeln(Options). ex_quant :- Sol = "3.14", Res = "3.141", match_quantities(Sol, Res, Comment), writeln(Sol), writeln(Res), writeln(Comment). ex_quant :- Sol = "3.141", Res = "3.14", match_quantities(Sol, Res, Comment), writeln(Sol), writeln(Res), writeln(Comment). ex_quant :- Sol = "3.14", Res = "3.14", match_quantities(Sol, Res, Comment), writeln(Sol), writeln(Res), writeln(Comment). </div> </div>