Return a string instead of unit - ocaml

I have a class method
method return__string_sCorona =
let est_vide chaine_de_car = List.exists ((=) "") strg in
let print_data name value = match nom with
| [] -> ()
| _ -> if not (is_empty name) then print_string value else () in
begin
print_data [self#get_province_state("Province or state: " ^ self#get_province_state ^ "\n");
print_data [self#get_country_region] ("Country or region: " ^ self#get_country_region ^ "\n");
print_data [self#get_last_update] ("Last Update: " ^ self#get_last_update ^ "\n");
print_string("Lat et Long: " ^ string_of_float self#get_lat ^ " " ^ string_of_float self#get_long ^ "\n");
print_string("Confirmed cases: " ^ string_of_int self#get_confirmed_cases ^ "\n");
print_string("Death cases: " ^ string_of_int self#get_death_cases ^ "\n");
print_string("Reestablished cases : " ^ string_of_int self#get_reestablished_cases ^ "\n");
print_string("Active cases: " ^ string_of_int self#get_active_cases ^ "\n\n");
end
which will print different information about Covid-19.
Actually, that method has type unit, but for some reasons, I need it return a string.
Instead of doing just calling my_object#return_string_sCorona, how can I return the string and print the result with print_string (my_object#return_string_sCorona)? It is important because I have to use it inside an interface using labltk.

Add this to the beginning of the method
let buf = Buffer.create 512 in
. . .
Change all calls print_string s to Buffer.add_string buf s.
The end of the method would look like this:
. . .
Buffer.add_string buf
("Active cases: " ^ string_of_int self#get_active_cases ^ "\n\n");
Buffer.contents buf
end
Update
Here's an example function that uses a buffer to accumulate a result. You should be able to rewrite your method in this style without too much trouble.
let show_intset x =
let buf = Buffer.create 128 in
let rec loop x n =
if x <> 0 then
begin
if x mod 2 = 1 then
begin
if Buffer.contents buf <> "" then
Buffer.add_string buf ", ";
Buffer.add_string buf (string_of_int n)
end;
loop (x / 2) (n + 1)
end
in
loop x 0;
Buffer.contents buf
Here's how it looks when you run the function in the toplevel:
# #use "p.ml";;
val show_intset : int -> string = <fun>
# show_intset 12;;
- : string = "2, 3"
# show_intset 1023;;
- : string = "0, 1, 2, 3, 4, 5, 6, 7, 8, 9"
# show_intset 1024;;
- : string = "10"
I hope this is helpful.

Related

This kind of expression is not allowed as right-hand side of `let rec'

I'm implementing the Raft protocol and my code is as follows:
let rec request_vote_loop: int =
match myState.myRole with
| Follower -> 0
| Leader -> 1
| Candidate ->
let trigger = Domain.spawn(fun _ -> Chan.send c TriggerEvent) in
let request_vote_daemon = Domain.spawn(fun _ ->
let rec loop n =
if n = 0 then 0
else let msg = Chan.recv votes in
match msg with
| (status, id) ->
Domain.join (Array.get !arr id);
if status = 1 then (Array.get votePeers id) := true; (Chan.send c ReceiveVoteEvent); loop (n - 1)
in loop ((Array.length (!peers)) / 2 + 1 - !current_vote)) in
let evt = Chan.recv c in
match evt with
| TimeoutEvent -> myState.myRole <- Follower; 3
| AppendEntriesEvent(_) ->
myState.myRole <- Follower; 4
| ReceiveVoteEvent ->
if !current_vote > (Array.length (!peers) / 2) then
begin current_vote := !current_vote + 1; myState.myRole <- Leader; 3 end
else current_vote := !current_vote + 1; request_vote_loop
| TriggerEvent ->
arr := Array.make (Array.length (!peers)) (Domain.spawn (fun i ->
if (!(Array.get votePeers i)) then 0
else
let conn = Array.get !peers i in
Lwt_main.run
(let+ resp = call_server conn
(RequestVoteArg({
candidateNumber = myState.myPersistentState.id;
term = myState.myPersistentState.currentTerm;
lastlogIndex = (Array.get myState.myPersistentState.logs ((Array.length myState.myPersistentState.logs) - 1)).index;
lastlogTerm = (Array.get myState.myPersistentState.logs ((Array.length myState.myPersistentState.logs) - 1)).term
})) in (match resp with
| Error(s) -> Chan.send votes (0, i); Printf.printf "requestVote: connection failed: %s" s; 1
| Ok(repl, s) ->
(match repl with
| RequestVoteRet(repl) ->
if repl.voteGranted then begin Chan.send votes (1, i); Printf.printf "requestVote: status: %s, currentVote: %d" s !current_vote; 2 end
else
if not (repl.term = (-1l)) then begin myState.myPersistentState.currentTerm <- repl.term; Chan.send votes (0, i);
Printf.printf "requestVote failed because of term: status: %s, currentVote: %d" s !current_vote; 3 end
else Chan.send votes (0, i); Printf.printf "requestVote failed: status: %s" s; 4
| _ -> failwith "Should not reach here" ))))); request_vote_loop
| _ -> failwith "Should not reach here"
in print_endline (Int.to_string request_vote_loop)
But there's an error that "This kind of expression is not allowed as right-hand side of `let rec'", it said my function is of type unit. I don't know what happened...
Thanks in advance.
Your definition starts like this:
let rec request_vote_loop: int = ...
This doesn't define a function, it defines a simple value of type int. The reason is that there are no parameters given.
There's too much code to process (and furthermore it's not self-contained). But I suspect you want to define a function that doesn't take any parameters. The way to do this is to pass () (known as unit) as the parameter:
let rec request_vote_loop () : int = ...
The recursive calls look like this:
request_vote_loop ()
The final call looks like this:
Int.to_string (request_vote_loop ())

OCaml Reading from file and perform some validation

can you help me out, i made this program to get an output from some .txt file like this :
john:3:uk
paul:18:us
#load "str.cma"
let f_test = "/home/test.txt" ;;
(*
Recursive Reading function
*)
let read_lines f_test : string list =
if Sys.file_exists (f_test) then
begin
let ic = open_in f_test in
try
let try_read () =
try Some (input_line ic) with End_of_file -> None in
let rec loop acc = match try_read () with
| Some s -> loop (s :: acc)
| None -> close_in_noerr ic; List.rev acc in
loop []
with e ->
close_in_noerr ic;
[]
end
else
[]
;;
(*Using Records*)
type user =
{
name : string;
age : int;
country : string;
};;
(*
Function to separated info in list
*)
let rec splitinfo ?(sep=":") l = match l with
| [] -> []
| x::xs -> (Str.split (Str.regexp ":") x)::splitinfo xs;;
(*
Function to get users position
*)
let get_user l:user =
let age = int_of_string (List.nth l 1) in
let user_name = List.nth l 0 in
{
name = user_name;
age = age ;
country = List.nth l 2;
};;
(*
Function to check some parameter is valid
*)
let par1 u: int =
if (u.age = 3) then
1
else
0;;
(*
Reporting function
*)
let report_statistics list_users =
let child = ref 0 in
let teenager = ref 0 in
let adult = ref 0 in print_string (" ----- -- Stats -- ----- \n" ) ;
List.iter (
fun user_l -> (
match user_l with
| [] -> print_string("> no user <\n")
| _ ->
let user = get_user user_l in
if (par1 user = 1) then (
print_string (" "^ user.name ^" --> Child \n" ) ;
child := !child + 1;
)
else
print_string (" "^ user.name ^" --> Other \n" );
)
) list_users;
print_string ("------- List ---- ");
print_newline();
print_string ("Child " );
print_int(!child);
print_newline();
print_string ("Teenager ") ;
print_int(!teenager);
print_newline();
print_string ("Adult ");
print_int(!adult);
print_newline();
;;
The program compile but doesn't output any result ...
What am i missing ?
I kept the function to check parameters simple so i can understand it better but can't figure it out why it isn't outputing any result
Can you help me out here ?
Thanks in advance :)
The code as given defines some functions such as read_lines and report_statistics. But there are no calls to these functions.
If there is no other OCaml source involved, this is probably your problem. You need to call the functions.
It is fairly customary to have a "main" function that does the work of an OCaml program, and then (this is key) you have to actually call the main function:
let main () =
(* Call the functions that do the work of the program *)
let () = main ()
I have many times forgotten this last line and then nothing happens when I run the program.

How to append to string in ocaml?

I don't know how to add some string to itself in loop.
let parameters = [| [| ("name", "fdjks"); ("value", "dsf") |]; [| ("name", "&^%"); ("value", "helo") |] |] ;;
let boundary = "----WebKitFormBoundary7MA4YWxkTrZu0gW";;
let body = "";;
for x = 0 to (Array.length(parameters) : int)-1 do
let (_, paramName) = parameters.(x).(0) in
let (_, paramValue) = parameters.(x).(1) in
body = body ^ "--" ^ boundary ^ "\r\n" ^ "Content-Disposition:form-data; name=\"" ^ paramName ^ "\"\r\n\r\n" ^ paramValue ;
print_endline(body)
done;;
but this gives error..
Any way to do this......?
The (^) operator concatenates two strings, e.g.,
# "hello" ^ ", " ^ "world!";;
- : string = "hello, world!"
If you have a list of strings, then you can use the String.concat function, that takes a separator, and a list of strings and produces there concatentation in an effective way:
# String.concat ", " ["hello"; "world"];;
- : string = "hello, world"
Why is using the (^) operator in a cycle is a bad idea? Every concatentation creates a new string and then copies the contents of both strings into the new string. So appending N strings will end up in approximately n^2 copying (where n is the length of a string). The same is true in Java and other languages/libraries where concatenation returns a new string, instead of mutating one of its arguments. A usual solution is to use the StringBuilder pattern, which in OCaml is represented with the Buffer module. So suppose, you don't have the String.concat function, and you would like to build your own efficient concatenation function (this could also be useful, since Buffer is a more general solution than String.concat, and will work when, for example, you input is not a list). Here is our implementation,
let concat xs =
let buf = Buffer.create 16 in
List.iter (Buffer.add_string buf) xs;
Buffer.contents buf
This function will create a buffer that will automatically resize itself. The 16 is just an initial guess and could be any number. On the second line we just iterate over all strings and push the to the buffer, and finally, we ask the buffer to build the resulting string. Here is how we use this function:
# concat ["hello"; ", "; "world"];;
- : string = "hello, world"
In OCaml variables are immutable by default. You need to use a reference if you want to change the value of a variable.
let parameters = [| [| ("name", "fdjks"); ("value", "dsf") |]; [| ("name", "&^%"); ("value", "helo") |] |] ;;
let boundary = "----WebKitFormBoundary7MA4YWxkTrZu0gW";;
let body = ref "";;
for x = 0 to (Array.length(parameters) : int)-1 do
let (_, paramName) = parameters.(x).(0) in
let (_, paramValue) = parameters.(x).(1) in
body := !body ^ "--" ^ boundary ^ "\r\n" ^ "Content-Disposition:form-data; name=\"" ^ paramName ^ "\"\r\n\r\n" ^ paramValue ;
print_endline(!body)
done;;
See also add elements to list in a loop in OCaml.
Please note that adding two strings in OCaml is an O(n) operation (n = number of characters), making it rather expensive for long strings. You can use the Buffer module to concatenate strings efficiently. Using ^, however, is much more readable.
A good compromise between readability and efficiency is probably, using the Buffer module for long and using ^ for short strings. Hence, using a Buffer for your outer iteration would be a good idea. The code looks as follows.
let parameters = [| [| ("name", "fdjks"); ("value", "dsf") |];
[| ("name", "&^%"); ("value", "helo") |] |] ;;
let boundary = "----WebKitFormBoundary7MA4YWxkTrZu0gW";;
let body = Buffer.create 256;;
for x = 0 to (Array.length(parameters) : int)-1 do
let (_, paramName) = parameters.(x).(0) in
let (_, paramValue) = parameters.(x).(1) in
Buffer.add_string body
("--" ^ boundary ^ "\r\n"
^ "Content-Disposition:form-data; name=\""
^ paramName ^ "\"\r\n\r\n" ^ paramValue);
print_endline(Buffer.contents body)
done;;
Iterating recursively with an accumulator is the idiomatic way to do what you want in ocaml:
let parameters = [| [| ("name", "fdjks"); ("value", "dsf") |]; [| ("name", "&^%"); "value", "helo") |] |] ;;
let boundary = "----WebKitFormBoundary7MA4YWxkTrZu0gW" ;;
let len = Array.length(parameters) ;;
let rec loop accum index =
if index < len then
let (_, paramName) = parameters.(index).(0) in
let (_, paramValue) = parameters.(index).(1) in
loop (accum ^ "--" ^ boundary ^ "\r\n"
^ "Content-Disposition:form-data; name=\""
^ paramName ^ "\"\r\n\r\n" ^ paramValue)
(index + 1)
else print_endline accum
in
loop "" 0 ;;
Or better still look at the Array.fold_left function.
let parameters = [| [| ("name", "fdjks"); ("value", "dsf") |]; [| ("name", "&^%"); ("value", "helo") |] |] ;;
let boundary = "----WebKitFormBoundary7MA4YWxkTrZu0gW";;
let () = print_endline
(Array.fold_left
(fun acc elt ->
let (_, paramName) = elt.(0) in
let (_, paramValue) = elt.(1) in
acc ^ "--" ^ boundary ^ "\r\n"
^ "Content-Disposition:form-data; name=\""
^ paramName ^ "\"\r\n\r\n" ^ paramValue)
""
parameters)

ocaml stringOfList using concat

The code I currently have is this where I'm testing stringOfList function
let rec sepConcat sep sl = match sl with
| [] -> ""
| h :: t ->
let f a x = a^sep^x in
let base = h in
let l = t in
List.fold_left f base l
let stringOfList f l = sepConcat "; " (List.map f l)
The output I'm supposed to get is
# stringOfList string_of_int [1;2;3;4;5;6];;
- : string = "[1; 2; 3; 4; 5; 6]"
but I'm getting
# stringOfList string_of_int [1;2;3;4;5;6];;
- : string = "1; 2; 3; 4; 5; 6"
What am I doing wrong? How can i add the extra [] inside the parentheses. Sepconcat is supposed to do this
# sepConcat ", " ["foo";"bar";"baz"];;
- : string = "foo, bar, baz"
# sepConcat "---" [];;
- : string = ""
# sepConcat "" ["a";"b";"c";"d";"e"];;
- : string = "abcde"
# sepConcat "X" ["hello"];;
- : string = "hello"
You're not doing anything wrong, there's just no place in the code that adds the square bracket characters.
I'd say that the square brackets are another punctuation thing like your sep parameter. You might want to pass them in as additional parameters at some level. Seems like it would be OK to add them as parameters to sepConcat--then it would handle all the punctuation.
Edit: it's not going to help to pass different values as sep. The separator goes between the strings. From your description, you want the extra brackets to go around the outside of the result. Not a separator.
Edit: The " characters are not part of the string. They are OCaml's way of showing you that the value is a string.
# let x = "a";;
val x : string = "a"
# x;;
- : string = "a"
# String.length x;;
- : int = 1
#
Any string value will have "" around it when printed by the interpreter. So there's no way the square brackets can be outside them!
Change your stringOfList to
let stringOfList f l =
let str = sepConcat "; " (List.map f l) in
"["^str^"]"
# stringOfList string_of_int [1;2;3;4;5];;
- : string = "[1; 2; 3; 4; 5]"

Haskell Alex - regex matches wrong string?

I'm trying to write lexer for an indentation-based grammar and I'm having trouble matching the indentation.
Here's my code:
{
module Lexer ( main ) where
import System.IO.Unsafe
}
%wrapper "monadUserState"
$whitespace = [\ \t\b]
$digit = 0-9 -- digits
$alpha = [A-Za-z]
$letter = [a-zA-Z] -- alphabetic characters
$ident = [$letter $digit _] -- identifier character
$indent = [\ \t]
#number = [$digit]+
#identifier = $alpha($alpha|_|$digit)*
error:-
#identifier { mkL LVarId }
\n $whitespace* \n { skip }
\n $whitespace* { setIndent }
$whitespace+ { skip }
{
data Lexeme = Lexeme AlexPosn LexemeClass (Maybe String)
instance Show Lexeme where
show (Lexeme _ LEOF _) = " Lexeme EOF"
show (Lexeme p cl mbs) = " Lexeme class=" ++ show cl ++ showap p ++ showst mbs
where
showap pp = " posn=" ++ showPosn pp
showst Nothing = ""
showst (Just s) = " string=" ++ show s
instance Eq Lexeme where
(Lexeme _ cls1 _) == (Lexeme _ cls2 _) = cls1 == cls2
showPosn :: AlexPosn -> String
showPosn (AlexPn _ line col) = show line ++ ':': show col
tokPosn :: Lexeme -> AlexPosn
tokPosn (Lexeme p _ _) = p
data LexemeClass
= LVarId
| LTIndent Int
| LTDedent Int
| LIndent
| LDedent
| LEOF
deriving (Show, Eq)
mkL :: LexemeClass -> AlexInput -> Int -> Alex Lexeme
mkL c (p, _, _, str) len = return (Lexeme p c (Just (take len str)))
data AlexUserState = AlexUserState { indent :: Int }
alexInitUserState :: AlexUserState
alexInitUserState = AlexUserState 0
type Action = AlexInput -> Int -> Alex Lexeme
getLexerIndentLevel :: Alex Int
getLexerIndentLevel = Alex $ \s#AlexState{alex_ust=ust} -> Right (s, indent ust)
setLexerIndentLevel :: Int -> Alex ()
setLexerIndentLevel i = Alex $ \s#AlexState{alex_ust=ust} -> Right (s{alex_ust=(AlexUserState i)}, ())
setIndent :: Action
setIndent input#(p, _, _, str) i = do
--let !x = unsafePerformIO $ putStrLn $ "|matched string: " ++ str ++ "|"
lastIndent <- getLexerIndentLevel
currIndent <- countIndent (drop 1 str) 0 -- first char is always \n
if (lastIndent < currIndent) then
do setLexerIndentLevel currIndent
mkL (LTIndent (currIndent - lastIndent)) input i
else if (lastIndent > currIndent) then
do setLexerIndentLevel currIndent
mkL (LTDedent (lastIndent - currIndent)) input i
else alexMonadScan
where
countIndent str total
| take 1 str == "\t" = do skip input 1
countIndent (drop 1 str) (total+1)
| take 4 str == " " = do skip input 4
countIndent (drop 4 str) (total+1)
| otherwise = return total
alexEOF :: Alex Lexeme
alexEOF = return (Lexeme undefined LEOF Nothing)
scanner :: String -> Either String [Lexeme]
scanner str =
let loop = do
tok#(Lexeme _ cl _) <- alexMonadScan
if (cl == LEOF)
then return [tok]
else do toks <- loop
return (tok:toks)
in runAlex str loop
addIndentations :: [Lexeme] -> [Lexeme]
addIndentations (lex#(Lexeme pos (LTIndent c) _):ls) =
concat [iter lex c, addIndentations ls]
where iter lex c = if c == 0 then []
else (Lexeme pos LIndent Nothing):(iter lex (c-1))
addIndentations (lex#(Lexeme pos (LTDedent c) _):ls) =
concat [iter lex c, addIndentations ls]
where iter lex c = if c == 0 then []
else (Lexeme pos LDedent Nothing):(iter lex (c-1))
addIndentations (l:ls) = l:(addIndentations ls)
addIndentations [] = []
main = do
s <- getContents
return ()
print $ fmap addIndentations (scanner s)
}
Problem is that in line \n $whitespace* { setIndent }, regex matches wrong string and calls setIndent with this wrong string. For debugging purposes, I added unsafePerformIO in setIndent function, here's an example run of the program:
begin
first indent
|matched string:
first indent
second indent
second indent
dedent
dedent
|
|matched string:
second indent
dedent
|
|matched string:
dedent
|
|matched string:
|
Right [ Lexeme class=LVarId posn=1:1 string="begin", Lexeme class=LIndent posn=1:6, Lexeme class=LVarId posn=2:15 string="indent", Lexeme class=LIndent posn=2:21, Lexeme class=LDedent posn=3:30, Lexeme class=LDedent posn=3:30, Lexeme class=LVarId posn=4:1 string="dedent", Lexeme EOF]
So setIndent is called with more than just whitespaces. And after it returns the lexeme for indentation, other part of the string is omitted.
Is this a bug in Alex? Or what am I doing wrong?
So I haven't analysed your code in detail, but I did notice this:
setIndent :: Action
setIndent input#(p, _, _, str) i = do
--let !x = unsafePerformIO $ putStrLn $ "|matched string: " ++ str ++ "|"
Note that str is the rest of the input, not just the current token. To get the current token, you want take i str. Perhaps this is giving you the impression that the token is matching more of the input than it really is.
We handle indentation in GHC's own lexer of course, so you might want to look there for ideas (although as you might expect it's rather large and complicated).