How to get a formatted string in OCaml? - ocaml

In OCaml, I can use Printf.printf to output formatted string, like
Printf.printf "Hello %s %d\n" world 123
However, printf is a kind of output.
What I wish for is not for output, but for a string. For example, I want
let s = something "Hello %s %d\n" "world" 123
then I can get s = "Hello World 123"
How can I do that?

You can use Printf.sprintf:
# Printf.sprintf "Hello %s %d\n" "world" 123;;
- : string = "Hello world 123\n"

You can do this:
$ ocaml
OCaml version 4.00.1
# let fmt = format_of_string "Hello %s %d";;
val fmt : (string -> int -> '_a, '_b, '_c, '_d, '_d, '_a) format6 = <abstr>
# Printf.sprintf fmt "world" 123;;
- : string = "Hello world 123"
The format_of_string function (as the name implies) transforms a string literal into a format. Note that formats must ultimately be built from string literals, because there is compiler magic involved. You can't read in a string and use it as a format, for example. (It wouldn't be typesafe.)

Related

How can I have String.tokens use more than one delimiter?

Because String.tokens is a curried function, I know I can change
String.tokens (fn c =\> c = #" ") "hello world";
to a string that would contain all the delimiters, but I am just confused about the actual dictation of how.
One of the forms that I tried was:
fun splitter nil = nil
| splitter str =
let
val c = " ,.;?:!\t\n"
val s = String.tokens (fn (c:string,x:char) => c=Char.toString c x) str
in
s
end;
With c being the string of the delimiters, but I know something is very wrong. If anyone could point me into the right direction that would be greatly appreciated.
String.tokens takes two arguments: a predicate to determine if a character is a token; and a string to split. The first argument is the important part. We don't have to specify a character to split on, just a rule to identify that character.
If you turn a string containing the token characters into a list with String.explode, then it's easy to use List.exists to find out if a character is in that token string.
fun splitOn(str, tokens) =
let
val tokens' = String.explode tokens
fun isToken c = List.exists (fn c' => c = c') tokens'
in
String.tokens isToken str
end;
splitOn("hello world | wooble. foo? bar!", " |.?!");
(* ["hello", "world", "wooble", "foo", "bar"] *)

Why can I not print this input a second time in OCaml?

I am very new to OCaml and am attempting to learn and write a program at the same time. I am writing a palindrome program. I am attempting to get a string from the user such as d e v e d or Hello World! or loud all of the preceding are valid user input. I need to read these strings and display them then reverse them and check if it is a palindrome or not. I did the following code...
print_string "Enter a string: ";
let str = read_line () in
Printf.printf "%s\n" str;;
Printf.printf "%s\n" str;;
this works fine and will give the print, Enter a string: d e v e d or Enter a string: Hello World! The issue comes when I try to add another Printf.printf "%s\n" str;; into the code. it gives me an error of File "main.ml", line 5, characters 21-24:
Error: Unbound value str with line 5 being the line of the 2nd Printf.printf statement. I have tried this with no ; for both of the print statements, with 1 or with 2 and I get the same error each time. Does anyone with more OCaml knowledge know why I get this error.
Because of your use of in your code parses as:
(let str = read_line () in Printf.printf "%s\n" str);;
and then a completely separate:
Printf.printf "%s\n" str;;
So str is local to the first printf.
You want:
let str = read_line ();;
Printf.printf "%s\n" str;;
Printf.printf "%s\n" str;;
which is three separate definitions. The first defines a global variable str.

Replacing the 1st regex-match group instead of the 0th

I was expecting this
val string = "hello , world"
val regex = Regex("""(\s+)[,]""")
println(string.replace(regex, ""))
to result in this:
hello, world
Instead, it prints this:
hello world
I see that the replace function cares about the whole match. Is there a way to replace only the 1st group instead of the 0th one?
Add the comma in the replacement:
val string = "hello , world"
val regex = Regex("""(\s+)[,]""")
println(string.replace(regex, ","))
Or, if kotlin supports lookahead:
val string = "hello , world"
val regex = Regex("""\s+(?=,)""")
println(string.replace(regex, ""))
You can retrieve the match range of the regular expression by using the groups property of MatchGroupCollection and then using the range as a parameter for String.removeRange method:
val string = "hello , world"
val regex = Regex("""(\s+)[,]""")
val result = string.removeRange(regex.find(string)!!.groups[1]!!.range)

F# - Using File.ReadAllLines how is it posible to get a string without new lines

The problem is that I have a string like:
public class MyFirstJavaProgram {
public static void main ( String []args ) {
System.out.println ( "Hello World" );
}
}
When I try to get a string list like:
["public";"class";"MyFirstJavaProgram";...;"(";""Hello World"";")";"...]
Im getting
["public class MyFirstJavaProgram {"; "";
" public static void main ( String []args) {";
" System.out.println("Hello World"); "; " }"; "}"]
How can i remove those white spaces.
Given the sample output in the original question, I think the closest simple solution would be:
File.ReadAllText("MyFirstJavaProgram.java").Split([|' '; '\n'|], StringSplitOptions.RemoveEmptyEntries)
|> Array.map (fun s -> s.Trim())
However, this will not treat the string "Hello World" as one entry in the array. For that, you would need to use a proper tokenization algorithm.
If you are not interested in some lines, the way to go is filter:
File.ReadAllLines(...)
|> Seq.filter (not << String.IsNullOrWhiteSpace)
Then, you can split all lines:
|> Seq.collect (fun line -> line.Split([| " " |], StringSplitOptions.RemoveEmptyEntries))
RemoveEmptyEntries will remove empty lines and leading/trailing whitespace. So filtering is not needed anymore and no trimming is neccessary.
Note that this is not a proper Java tokenizer though, e.g. main( won't be split into main and ( but "Hello World" will be split into "Hello and World".
For doing proper Java parsing, look for a library.
Note that asking for and recommending libraries is not appropriate on StackOverflow. SoftwareRecommendations might be able to help.

Concatenate strings in ocaml with newline between them

I'd like to do something like this
String.concat '\n' [str1; str2 ... strn]
so I can print in a file. But ocaml doesn't allow me to do that. What can I do?
String.concat "\n" [str1; str2 ... strn]
works fine. The problem is that you used '\n', which is a character literal, not a string. Example:
# String.concat '\n' ["abc"; "123"];;
Error: This expression has type char but an expression was expected of type
string
# String.concat "\n" ["abc"; "123"];;
- : string = "abc\n123"
If you're using Jane Street's base module for your standard library you'll have to do it like so:
# #require "base";;
# open! Base;;
# String.concat ~sep:"\n" ["abc"; "123"];;
- : string = "abc\n123"
Jane Street really likes to take advantage of named arguments.