I am newbie to SML, trying to write recursive program to delete chars from a string:
remCharR: char * string -> string
So far wrote this non-recursive prog. Need help to write recursive one.
- fun stripchars(string,chars) = let
= fun aux c =
= if String.isSubstring(str c) chars then
= ""
= else
= str c
= in
= String.translate aux string
= end
= ;
You have already found a very idiomatic way to do this. Explicit recursion is not a goal in itself, except perhaps in a learning environment. That is, explicit recursion is, compared to your current solution, encumbered with a description of the mechanics of how you achieve the result, but not what the result is.
Here is one way you can use explicit recursion by converting to a list:
fun remCharR (c, s) =
let fun rem [] = []
| rem (c'::cs) =
if c = c'
then rem cs
else c'::rem cs
in implode (rem (explode s)) end
The conversion to list (using explode) is inefficient, since you can iterate the elements of a string without creating a list of the same elements. Generating a list of non-removed chars is not necessarily a bad choice, though, since with immutable strings, you don't know exactly how long your end-result is going to be without first having traversed the string. The String.translate function produces a list of strings which it then concatenates. You could do something similar.
So if you replace the initial conversion to list with a string traversal (fold),
fun fold_string f e0 s =
let val max = String.size s
fun aux i e =
if i < max
then let val c = String.sub (s, i)
in aux (i+1) (f (c, e))
end
else e
in aux 0 e0 end
you could then create a string-based filter function (much alike the String.translate function you already found, but less general):
fun string_filter p s =
implode (fold_string (fn (c, res) => if p c then c::res else res) [] s)
fun remCharR (c, s) =
string_filter (fn c' => c <> c') s
Except, you'll notice, it accidentally reverses the string because it folds from the left; you can fold from the right (efficient, but different semantics) or reverse the list (inefficient). I'll leave that as an exercise for you to choose between and improve.
As you can see, in avoiding String.translate I've built other generic helper functions so that the remCharR function does not contain explicit recursion, but rather depends on more readable high-level functions.
Update: String.translate actually does some pretty smart things wrt. memory use.
Here is Moscow ML's version of String.translate:
fun translate f s =
Strbase.translate f (s, 0, size s);
with Strbase.translate looking like:
fun translate f (s,i,n) =
let val stop = i+n
fun h j res = if j>=stop then res
else h (j+1) (f(sub_ s j) :: res)
in revconcat(h i []) end;
and with the helper function revconcat:
fun revconcat strs =
let fun acc [] len = len
| acc (v1::vr) len = acc vr (size v1 + len)
val len = acc strs 0
val newstr = if len > maxlen then raise Size else mkstring_ len
fun copyall to [] = () (* Now: to = 0. *)
| copyall to (v1::vr) =
let val len1 = size v1
val to = to - len1
in blit_ v1 0 newstr to len1; copyall to vr end
in copyall len strs; newstr end;
So it first calculates the total length of the final string by summing the length of each sub-string generated by String.translate, and then it uses compiler-internal, mutable functions (mkstring_, blit_) to copy the translated strings into the final result string.
You can achieve a similar optimization when you know that each character in the input string will result in 0 or 1 characters in the output string. The String.translate function can't, since the result of a translate can be multiple characters. So an alternative implementation uses CharArray. For example:
Find the number of elements in the new string,
fun countP p s =
fold_string (fn (c, total) => if p c
then total + 1
else total) 0 s
Construct a temporary, mutable CharArray, update it and convert it to string:
fun string_filter p s =
let val newSize = countP p s
val charArr = CharArray.array (newSize, #"x")
fun update (c, (newPos, oldPos)) =
if p c
then ( CharArray.update (charArr, newPos, c) ; (newPos+1, oldPos+1) )
else (newPos, oldPos+1)
in fold_string update (0,0) s
; CharArray.vector charArr
end
fun remCharR (c, s) =
string_filter (fn c' => c <> c') s
You'll notice that remCharR is the same, only the implementation of string_filter varied, thanks to some degree of abstraction. This implementation uses recursion via fold_string, but is otherwise comparable to a for loop that updates the index of an array. So while it is recursive, it's also not very abstract.
Considering that you get optimizations comparable to these using String.translate without the low-level complexity of mutable arrays, I don't think this is worthwhile unless you start to experience performance problems.
Related
I'm used to JaneStreet's Core library. Its List module has a neat init function:
List.init;;
- : int -> f:(int -> 'a) -> 'a list = <fun>
It allows you to create a list with using a custom function to initialize elements:
List.init 5 ~f:(Fn.id);;
- : int list = [0; 1; 2; 3; 4]
List.init 5 ~f:(Int.to_string);;
- : string list = ["0"; "1"; "2"; "3"; "4"]
However, this function doesn't seem to exist in Pervasives, which is sad. Am I missing something, or do I have to implement it myself? And if I do need to write it, how do I achieve this?
EDIT:
I have written an imperative version of init, but it doesn't feel right to have to resort to OCaml's imperative features in such a case. :(
let init n ~f =
let i = ref 0 in
let l = ref [] in
while !i < n do
l := (f !i) :: !l;
incr i;
done;
List.rev !l
;;
EDIT 2:
I've opened a pull request on OCaml's GitHub to have this feature included.
EDIT 3:
The feature was released in OCaml 4.06.
A recursive implementation is fairly straightforward. However, it is not tail-recursive, which means that you'll risk a stack overflow for large lists:
let init_list n ~f =
let rec init_list' i n f =
if i >= n then []
else (f i) :: (init_list' (i+1) n f)
in init_list' 0 n f
We can transform it into a tail-recursive version using the usual techniques:
let init_list n ~f =
let rec init_list' acc i n f =
if i >= n then acc
else init_list' ((f i) :: acc) (i+1) n f
in List.rev (init_list' [] 0 n f)
This uses an accumulator and also needs to reverse the intermediate result, as the list is constructed in reverse. Note that we could also use f (n-i-1) instead of f i to avoid reversing the list, but this may lead to unexpected behavior if f has side-effects.
An alternative and shorter solution is to simply use Array.init as a starting point:
let init_list n ~f = Array.(init n f |> to_list)
You can copy the code from JaneStreet and use it.
The code look's like (but not exactly the same) :
let init n ~f =
if n < 0 then raise (Invalid_argument "init");
let rec loop i accum =
if i = 0 then accum
else loop (i-1) (f (i-1) :: accum)
in
loop n []
;;
You can find the original code inside core_list0.ml from the package core_kernel.
I am new to SML and don't understand too about the syntax.
I am doing a practice on checking palindrome without reversing the list. Here is my code
fun symmetric(i,n,inlist) =
if List.nth(inlist,i-1) = List.nth(inlist,n-i)
then true
else
false;
fun palindrome(n, inlist) =
let
val i = ref 1
in
while !i < n do (
if symmetric(!i,!n,!inlist) = false
then false
else ()
i := !i + 1
)
true
end;
I got errors in fun palindrome only, but can't fix it by myself.
You can even make a palindrome checker without converting your string to a list:
fun palindrome s =
let fun check i j =
i >= j orelse
String.sub (s, i) = String.sub (s, j) andalso
check (i+1) (i-1)
in check 0 (String.size s - 1) end
Here is some feedback for your code:
Naturally, consider using recursion rather than iteration.
A common problem for non-functional programmers is that they seem to want to execute many statements in succession only for their side-effect. In functional programming you rely very much on the value of every expression to guide the result of the program. There is a ; operator, though, and it could be used like this:
fun palindrome s =
let val i = ref 0
val j = ref (String.size s - 1)
val result = ref true
in while !i < !j do
(if String.sub (s, !i) = String.sub (s, !j)
then (i := !i + 1 ; j := !j - 1)
else (i := !j ; result := false))
; !result
end
Often, though, if you want to do multiple things in a row, let-expressions are just as neat as the ; operator.
The code
if A = false
then false
else B
can be written as
if not (A)
then false
else B
which can further be improved into
if A
then B
else false
which is really the same as
A andalso B
So the morals are:
Instead of A = false, write not A (and instead of A = true, write A).
You can always replace if ... then <true/false> else <true/false> with some combination of andalso and orelse. That is, if-then-else is never necessary when the result type is bool (but you might still prefer it if the logic is very convoluted).
If the restriction against reversing a list was intended to ban using the built-in rev but not computations which implicitly reverse lists, here is a stack-based approach. The idea is to push characters onto a stack (represented as a list), and then pop them off, checking them against the original list of characters. If either the stack or the original list empty first, or if the item popped doesn't match the corresponding char in the original list -- it isn't a plalindrome
fun pushAll [] stack = stack
| pushAll (x::xs) stack = pushAll xs (x::stack)
fun popCheck [] [] = true
| popCheck [] _ = false
| popCheck _ [] = false
| popCheck (x::xs) (y::ys) = x = y andalso popCheck xs ys
fun palindrome s =
let val chars = explode s
val stack = pushAll chars []
in
popCheck chars stack
end;
I have a character list [#"h", #"i", #" ", #"h", #"i"] which I want to get the first word from this (the first character sequence before each space).
I've written a function which gives me this warning:
stdIn:13.1-13.42 Warning: type vars not generalized because of value
restriction are instantiated to dummy types (X1,X2,...)
Here is my code:
fun next [] = ([], [])
| next (hd::tl) = if(not(ord(hd) >= 97 andalso ord(hd) <= 122)) then ([], (hd::tl))
else
let
fun getword [] = [] | getword (hd::tl) = if(ord(hd) >= 97 andalso ord(hd) <= 122) then [hd]#getword tl else [];
in
next (getword (hd::tl))
end;
EDIT:
Expected input and output
next [#"h", #"i", #" ", #"h", #"i"] => ([#"h", #"i"], [#" ", #"h", #"i"])
Can anybody help me with this solution? Thanks!
This functionality already exists within the standard library:
val nexts = String.tokens Char.isSpace
val nexts_test = nexts "hi hi hi" = ["hi", "hi", "hi"]
But if you were to build such a function anyway, it seems that you return ([], []) sometimes and a single list at other times. Normally in a recursive function, you can build the result by doing e.g. c :: recursive_f cs, but this is assuming your function returns a single list. If, instead, it returns a tuple, you suddenly have to unpack this tuple using e.g. pattern matching in a let-expression:
let val (x, y) = recursive_f cs
in (c :: x, y + ...) end
Or you could use an extra argument inside a helper function (since the extra argument would change the type of the function) to store the word you're extracting, instead. A consequence of doing that is that you end up with the word in reverse and have to reverse it back when you're done recursing.
fun isLegal c = ord c >= 97 andalso ord c <= 122 (* Only lowercase ASCII letters *)
(* But why not use one of the following:
fun isLegal c = Char.isAlpha c
fun isLegal c = not (Char.isSpace c) *)
fun next input =
let fun extract (c::cs) word =
if isLegal c
then extract cs (c::word)
else (rev word, c::cs)
| extract [] word = (rev word, [])
in extract input [] end
val next_test_1 =
let val (w, r) = next (explode "hello world")
in (implode w, implode r) = ("hello", " world")
end
val next_test_2 = next [] = ([], [])
When implementing algorithms in SML I often wonder if there is a simple way to make code that makes heavy use of arrays more readable. For instance if I define a SML function to swap 2 elements in an array, the code is ...
local open Array in
fun exch (a, i, j) =
let
val tmp = sub (a, i)
val _ = update (a, i, sub (a, j))
val _ = update (a, j, tmp)
in () end
end
What I would like to have is a more readable, cleaner Version like in this Scala-snippet ...
def exch[T](a: Array[T], i: Int, j: Int) {
val tmp = a(i)
a(i) = a(j)
a(j) = tmp
}
For something as simple as swapping 2 elements in an array, the SML version is okay. But as soon as the algorithms get more complex the code becomes more and more incomprehensible and does obfuscate the underlying algorithm.
A slightly more complex example would be this stack (implemented as resizable array) ...
structure ArrayStack = struct
type 'a stack = ('a option array * (int ref)) ref
exception Empty
fun mkStack () = ref (Array.array (1, NONE), ref 0)
fun isEmpty (ref (_, ref 0)) = true
| isEmpty _ = false
fun resize (array as ref (xs, n), capacity) =
let
val length = Array.length xs
in
array := (Array.tabulate (
capacity,
fn i => if i < length then Array.sub (xs, i) else NONE
), n)
end
fun push (array as ref (xs, n : int ref), x) =
if Array.length xs = !n then (
resize (array, !n*2)
; push (array, x))
else (
Array.update (xs, !n, SOME x)
; n := !n+1)
fun pop (ref (xs, ref 0)) = raise Empty
| pop (array as ref (xs, n : int ref)) = let
val _ = (n := !n-1)
val x = Array.sub (xs, !n)
val _ = Array.update (xs, !n, NONE)
val q = (Array.length xs) div 4
val _ = if !n > 0 andalso !n = q then resize (array, q) else ()
in
valOf x
end
end
By comparison with the java implementation at http://algs4.cs.princeton.edu/13stacks/ResizingArrayStack.java.html the implementation (especially of push/pop) becomes hard to read.
How can I make such code more readable?
It is true, arrays are rather awkward to use in SML. To some degree this is intentional, to discourage their use -- because most of the time, they are not the best choice of data structure. Your stack is a good example, since it is much better implemented as a list:
structure ListStack =
struct
type 'a stack = 'a list ref
fun stack () = ref nil
fun isEmpty s = List.null (!s)
fun push (s, x) = s := x::(!s)
fun pop s =
case !s of
nil => raise Empty
| x::xs => (s := xs; x)
end
(In fact, you wouldn't even normally do that, and avoid a stateful data structure like this altogether, using plain lists instead.)
If your concern is the allocation involved with lists, then note that (a) it is not doing more allocations than the array version (one :: instead of one SOME per push), and (b) allocations are very cheap in a language like SML.
But since your question is about using arrays, here is a slightly more idiomatic implementation of your array stack:
structure ArrayStack =
struct
open Array infix sub
datatype 'a stack = Stack of {data : 'a option array ref, size : int ref}
fun stack () = Stack {data = ref (array (1, NONE)), size = ref 0}
fun isEmpty (Stack {size, ...}) = !size = 0
fun resize (data, len') =
let val data' = array (len', NONE) in
copy {src = !data, dst = data', di = 0};
data := data'
end
fun push (Stack {data, size}, x) =
let val size' = !size + 1 in
if size' > length (!data) then resize (data, !size * 2) else ();
update (!data, !size, SOME x);
size := size'
end
fun pop (Stack {data, size}) =
if !size = 0 then raise Empty else
let
val _ = size := !size - 1
val x = !data sub (!size)
val q = length (!data) div 4
in
update (!data, !size, NONE);
if q > 0 andalso !size = q then resize (data, q) else ();
valOf x
end
end
In particular, I made sub infix, which allows you to write arr sub i. I did this just for demonstration, in this example it's not really worth it, with only one such usage.
I am trying to do something fairly simple. I want to take a string such as "1,000" and return the string "1000".
Here was my attempt:
String.map (function x -> if x = ',' then '' else x) "1,000";;
however I get a compiler error saying there is a syntax error wrt ''
Thanks for the insight!
Unfortunately, there's no character like the one you're looking for. There is a string that's 0 characters long (""), but there's no character that's not there at all. All characters (so to speak) are 1 character.
To solve your problem you need a more general operation than String.map. The essence of a map is that its input and output have the same shape but different contents. For strings this means that the input and output are strings of the same length.
Unless you really want to avoid imperative coding (which is actually a great thing to avoid, especially when starting out with OCaml), you would probably do best using String.iter and a buffer (from the Buffer module).
Update
The string_map_partial function given by Andreas Rossberg is pretty nice. Here's another implementation that uses String.iter and a buffer:
let string_map_partial f s =
let b = Buffer.create (String.length s) in
let addperhaps c =
match f c with
| None -> ()
| Some c' -> Buffer.add_char b c'
in
String.iter addperhaps s;
Buffer.contents b
Just an alternate implementation with different stylistic tradeoffs. Not faster, probably not slower either. It's still written imperatively (for the same reason).
What you'd need here is a function like the following, which unfortunately is not in the standard library:
(* string_map_partial : (char -> char option) -> string -> string *)
let string_map_partial f s =
let buf = String.create (String.length s) in
let j = ref 0 in
for i = 0 to String.length s - 1 do
match f s.[i] with
| None -> ()
| Some c -> buf.[!j] <- c; incr j
done;
String.sub buf 0 !j
You can then write:
string_map_partial (fun c -> if c = ',' then None else Some c) "1,000"
(Note: I chose an imperative implementation for string_map_partial, because a purely functional one would require repeated string concatenation, which is fairly expensive in OCaml.)
A purely functional version could be this one:
let string_map_partial f s =
let n = String.length s in
let rec map_str i acc =
if i < n then
map_str (i + 1) (acc ^ (f (String.make 1 s.[i])))
else acc
in map_str 0 ""
Which is terminal recursive, but less performant than the imperative version.