I know ML has a bunch of string methods (substring, etc) that would make this easier but I want to get more comfortable with the language, so I'm implementing some myself.
I'm trying to truncate a string, i.e. cut off the string after a certain number of characters. I think I'm very close but am getting a syntax error when I do
val x::xs = explode(myString);
Here's the full code:
fun getAllButLast([x]) = nil
| getAllButLast(x::xs) = x::getAllButLast(xs);
fun truncate(myString, 0) = ""
| truncate(myString, limit:int) =
let
val x::xs = explode(myString);
in
x::truncate(implode(getAllButLast(xs)), limit - 1)
end;
Thoughts on why the compiler doesn't like this?
val x::xs = explode(myString);
Thanks for the help,
bclayman
Edit to include error:
Ullman.sml:82.5-82.55 Error: operator and operand don't agree [tycon mismatch]
operator domain: char * char list
operand: char * string
in expression:
x :: truncate (implode (getAllButLast <exp>),limit - 1)
uncaught exception Error
raised at: ../compiler/TopLevel/interact/evalloop.sml:66.19-66.27
../compiler/TopLevel/interact/evalloop.sml:44.55
../compiler/TopLevel/interact/evalloop.sml:292.17-292.20
As the error message shows, it is complaining about a different line. And it is complaining because the right operand of the :: operator in that line (the result of the recursive call to truncate) is a string, not a list. You probably want to use ^ instead, which denotes string concatenation.
Hint: There are other issues with your code. At least it is extremely inefficient. You should generally avoid implode/explode, but if you must use them, you should at least only call each of them once for the whole string, and not once for every character in the recursion.
Related
Hi newbie here and I am trying to master recursive functions in Erlang. This function looks like it should work, but I cannot understand why it does not. I am trying to create a function that will take N and a string and will print out to stdout the string the number of times.
My code:
-module(print_out_n_times).
-export([print_it/2).
print_it(0, _) ->
"";
print_it(N, string) ->
io:fwrite(string),
print_it(N - 1, string).
The error I get is:
** exception error: no function clause matching print_it(5, "hello')
How can I make this work ?
Variables in Erlang start with a capital letter. string is an atom, not a variable named "string". When you define a function print_it(N, string), it can be called with any value for the first argument and only the atom string as the second. Your code should work if you replace string with String:
print_it(N, String) ->
io:fwrite(String),
print_it(N - 1, String).
I am currently learning sml but I have one question that I can not find an answer for. I have googled but still have not found anything.
This is my code:
fun diamond(n) =
if(n=1) then (
print("*")
) else (
print("*")
diamond(n-1)
)
diamond(5);
That does not work. I want the code to show as many * as number n is and I want to do that with recursion, but I don't understand how to do that.
I get an error when I try to run that code. This is the error:
Standard ML of New Jersey v110.78 [built: Thu Aug 20 19:23:18 2015]
[opening a4_p2.sml] a4_p2.sml:8.5-9.17 Error: operator is not a
function [tycon mismatch] operator: unit in expression:
(print "*") diamond /usr/local/bin/sml: Fatal error -- Uncaught exception Error with 0 raised at
../compiler/TopLevel/interact/evalloop.sml:66.19-66.27
Thank you
You can do side effects in ML by using ';'
It will evaluate whatever is before the ';' and discard its result.
fun diamond(n) =
if(n=1)
then (print "*"; 1)
else (print "*"; diamond(n-1));
diamond(5);
The reason for the error is because ML is a strongly typed language that although you don't need to specify types explicitly, it will infer them based on environmental factors at compile time. For this reason, every evaluation of functions, statements like if else need to evaluate to an unambiguous singular type.
If you were allowed to do the following:
if(n=1)
then 1
else print "*";
then the compiler will get a different typing for the then and else branch respectively.
For the then branch the type would be int -> int whereas the type for the else branch would be int -> unit
Such a dichotomy is not allowed under a strongly typed language.
As you need to evaluate to a singular type, you will understand that ML does not support the execution of a block of instructions as we commonly see in other paradigms which transposed to ML naively would render something like this:
....
if(n=1)
then (print "1"
print "2"
)
else (print "3"
diamond(n-1)
)
...
because what type would the then branch evaluate to? int -> unit? Then what about the other print statement? A statement has to return a singular result(even it be a compound) so that would not make sense. What about int -> unit * unit? No problem with that except that syntactically speaking, you failed to communicate a tuple to the compiler.
For this reason, the following WOULD work:
fun diamond(n) =
if(n=1)
then (print "a", 1) /* A tuple of the type unit * int */
else diamond(n-1);
diamond(5);
As in this case you have a function of type int -> unit * int.
So in order to satisfy the requirement of the paradigm of strongly typed functional programming where we strive for building mechanisms that evaluate to one result-type, we thus need to communicate to the compiler that certain statements are to be executed as instructions and are not to be incorporated under the typing of the function under consideration.
For this reason, you use ';' to communicate to the compiler to simply evaluate that statement and discard its result from being incorporated under the type evaluation of the function.
As far as your actual objective is concerned, following is a better way of writing the function, diamond as type int -> string:
fun diamond(n) =
if(n=1)
then "*"
else "*" ^ diamond(n-1);
print( diamond(5) );
The above way is more for debugging purposes.
I am trying to learn Erlang and I am working on the practice problems Erlang has on the site. One of them is:
Write the function time:swedish_date() which returns a string containing the date in swedish YYMMDD format:
time:swedish_date()
"080901"
My function:
-module(demo).
-export([swedish_date/0]).
swedish_date() ->
[YYYY,MM,DD] = tuple_to_list(date()),
string:substr((integer_to_list(YYYY, 3,4)++pad_string(integer_to_list(MM))++pad_string(integer_to_list(DD)).
pad_string(String) ->
if
length(String) == 1 -> '0' ++ String;
true -> String
end.
I'm getting the following errors when compiled.
demo.erl:6: syntax error before: '.'
demo.erl:2: function swedish_date/0 undefined
demo.erl:9: Warning: function pad_string/1 is unused
error
How do I fix this?
After fixing your compilation errors, you're still facing runtime errors. Since you're trying to learn Erlang, it's instructive to look at your approach and see if it can be improved, and fix those runtime errors along the way.
First let's look at swedish_date/0:
swedish_date() ->
[YYYY,MM,DD] = tuple_to_list(date()),
Why convert the list to a tuple? Since you use the list elements individually and never use the list as a whole, the conversion serves no purpose. You can instead just pattern-match the returned tuple:
{YYYY,MM,DD} = date(),
Next, you're calling string:substr/1, which doesn't exist:
string:substr((integer_to_list(YYYY,3,4) ++
pad_string(integer_to_list(MM)) ++
pad_string(integer_to_list(DD))).
The string:substr/2,3 functions both take a starting position, and the 3-arity version also takes a length. You don't need either, and can avoid string:substr entirely and instead just return the assembled string:
integer_to_list(YYYY,3,4) ++
pad_string(integer_to_list(MM)) ++
pad_string(integer_to_list(DD)).
Whoops, this is still not right: there is no such function integer_to_list/3, so just replace that first call with integer_to_list/1:
integer_to_list(YYYY) ++
pad_string(integer_to_list(MM)) ++
pad_string(integer_to_list(DD)).
Next, let's look at pad_string/1:
pad_string(String) ->
if
length(String) == 1 -> '0' ++ String;
true -> String
end.
There's a runtime error here because '0' is an atom and you're attempting to append String, which is a list, to it. The error looks like this:
** exception error: bad argument
in operator ++/2
called as '0' ++ "8"
Instead of just fixing that directly, let's consider what pad_string/1 does: it adds a leading 0 character if the string is a single digit. Instead of using if to check for this condition — if isn't used that often in Erlang code — use pattern matching:
pad_string([D]) ->
[$0,D];
pad_string(S) ->
S.
The first clause matches a single-element list, and returns a new list with the element D preceded with $0, which is the character constant for the character 0. The second clause matches all other arguments and just returns whatever is passed in.
Here's the full version with all changes:
-module(demo).
-export([swedish_date/0]).
swedish_date() ->
{YYYY,MM,DD} = date(),
integer_to_list(YYYY) ++
pad_string(integer_to_list(MM)) ++
pad_string(integer_to_list(DD)).
pad_string([D]) ->
[$0,D];
pad_string(S) ->
S.
But a simpler approach would be to use the io_lib:format/2 function to just format the desired string directly:
swedish_date() ->
io_lib:format("~w~2..0w~2..0w", tuple_to_list(date())).
First, note that we're back to calling tuple_to_list(date()). This is because the second argument for io_lib:format/2 must be a list. Its first argument is a format string, which in our case says to expect three arguments, formatting each as an Erlang term, and formatting the 2nd and 3rd arguments with a width of 2 and 0-padded.
But there's still one more step to address, because if we run the io_lib:format/2 version we get:
1> demo:swedish_date().
["2015",["0",56],"29"]
Whoa, what's that? It's simply a deep list, where each element of the list is itself a list. To get the format we want, we can flatten that list:
swedish_date() ->
lists:flatten(io_lib:format("~w~2..0w~2..0w", tuple_to_list(date()))).
Executing this version gives us what we want:
2> demo:swedish_date().
"20150829"
Find the final full version of the code below.
-module(demo).
-export([swedish_date/0]).
swedish_date() ->
lists:flatten(io_lib:format("~w~2..0w~2..0w", tuple_to_list(date()))).
UPDATE: #Pascal comments that the year should be printed as 2 digits rather than 4. We can achieve this by passing the date list through a list comprehension:
swedish_date() ->
DateVals = [D rem 100 || D <- tuple_to_list(date())],
lists:flatten(io_lib:format("~w~2..0w~2..0w", DateVals)).
This applies the rem remainder operator to each of the list elements returned by tuple_to_list(date()). The operation is needless for month and day but I think it's cleaner than extracting the year and processing it individually. The result:
3> demo:swedish_date().
"150829"
There are a few issues here:
You are missing a parenthesis at the end of line 6.
You are trying to call integer_to_list/3 when Erlang only defines integer_to_list/1,2.
This will work:
-module(demo).
-export([swedish_date/0]).
swedish_date() ->
[YYYY,MM,DD] = tuple_to_list(date()),
string:substr(
integer_to_list(YYYY) ++
pad_string(integer_to_list(MM)) ++
pad_string(integer_to_list(DD))
).
pad_string(String) ->
if
length(String) == 1 -> '0' ++ String;
true -> String
end.
In addition to the parenthesis error on line 6, you also have an error on line 10 where yo use the form '0' instead of "0", so you define an atom rather than a string.
I understand you are doing this for educational purpose, but I encourage you to dig into erlang libraries, it is something you will have to do. For a common problem like this, it already exists function that help you:
swedish_date() ->
{YYYY,MM,DD} = date(), % not useful to transform into list
lists:flatten(io_lib:format("~2.10.0B~2.10.0B~2.10.0B",[YYYY rem 100,MM,DD])).
% ~X.Y.ZB means: uses format integer in base Y, print X characters, uses Z for padding
I have an Ocaml function that is giving me errors.
What I am trying to do:
Recursively create a List of random numbers (0-2) of size "limit".
Here's what I have:
let rec carDoorNumbers = fun limit ->
match limit with
| [1] -> Random.int 3
| [] -> Random.int 3 :: carDoorNumbers (limit-1);;
I am getting this error:
Error: This expression has type 'a list
but an expression was expected of type int
Think about what your function has to do: given a limit, you have to create a list of numbers. So your type is something like carDoorNumbers : int -> int list.
Looking at that, it seems you have two errors. First, you're matching limit (which should be an int) against a list pattern. [1] -> ... matches a list containing only the element 1 and [] matches the empty list; you really want to match against the number 1 and any other number n.
The second error is that you return two different types in your match statement. Remember that you are supposed to be returning a list. In the first case, you are returning Random.int 3, which is an int rather than an int list. What you really want to return here is something like [Random.int 3].
The error you got is a little confusing. Since the first thing you returned was an int, it expects your second thing to also be an int. However, your second case was actually correct: you do return an int list! However, the compiler does not know what you meant, so its error is backwards; rather than changing the int list to an int, you need to change the int to an int list.
Your match expression treats limit like a list. Both [1] and [] are lists. That's what the compiler is telling you. But it seems limit should be an integer.
To match an integer, just use an integer constant. No square brackets.
(As a side comment, you might want to be sure the function works well when you pass it 0.)
[<ReflectedDefinition>]
let rec x = (fun() -> x + "abc") ()
The sample code with the recursive value above produces the following F# compiler error:
error FS0432: [<ReflectedDefinition>] terms cannot contain uses of the prefix splice operator '%'
I can't see any slicing operator usage in the code above, looks like a bug... :)
Looks like this is the problem with the quotation via ReflectedDefinitionAttribute only, normal quotation works well:
let quotation =
<# let rec x = (fun() -> x + "abc") () in x #>
produces expected result with the hidden Lazy.create and Lazy.force usages:
val quotation : Quotations.Expr<string> =
LetRecursive
([(x, Lambda (unitVar,
Application
(Lambda (unitVar0,
Call (None,
String op_Addition[String,String,String](String, String),
[Call (None,
String Force[String](Lazy`1[System.String]), // `
[x]), Value ("abc")])),
Value (<null>)))),
(x, Call (None, Lazy`1[String] Create[String](FSharpFunc`2[Unit,String]), [x])),
(x, Call (None, String Force[String](Lazy`1[String]), [x]))], x) // `
So the question is: is this an F# compiler bug or not?
I'd think that this may be caused by the treatment of recursive values in F#. As a workaround, you can turn the recursive reference into a parameter:
[<ReflectedDefinition>]
let foo x = (fun() -> x + "abc") ()
// To construct the recursive value, you'd write:
let rec x = foo x
The last line is of course invalid (just like your original code), because you're creating an immediate recursive reference, but it should give you the idea - in reality, you'd probably enclose x in a lambda function.
EDIT Originally, I thought that the problem may be as below, but I'm not sure now (see comments).
It looks more like a (probably known) limitation to me than an unexpected bug. There is an important difference between the two versions of the code you wrote - in the first case, you're binding a public value (visible to .NET) named x while in the second case, x is just a symbol used only in the quotation.
The quotation that would have to be stored in the meta-data of the assembly would look like this:
let rec x = <# (fun() -> %x + "abc") () #>
The body is quoted, but x is not a quoted symbol, so it needs to be spliced into the quotation (that is, it will be evaluated and the result will be used in its place). Note that this code will fail, because you're declaring a recursive value with immediate reference - x needs to be evaluated as part of its definition, so this won't work.
However, I think that % cannot appear in ReflectedDefinition quotations (that is, you cannot store the above in meta-data), because it involves some runtime aspects - you'd need to evaluate x when loading the meta-data.