Problems with Strings

classic Classic list List threaded Threaded
5 messages Options
Reply | Threaded
Open this post in threaded view
|

Problems with Strings

Pablo Dejuan
Hi. I'm new in this and I have dificulties using strings. I tried to do
an "inverese" function that should turn "bac" into "cab" but instead of
this I get [98,[97,[99,[]]]] which I noticed that are tha Ascii codes
for c, a, and b.

Here is the code:
inversa("") -> "";
inversa([Ini|Cont]) -> Inv= inversa(Cont), [Ini,Inv].

Thanks In Advance.



Reply | Threaded
Open this post in threaded view
|

Problems with Strings

Raimo Niskanen-7
inversa("") -> "";
inversa([Ini|Cont]) -> Inv= inversa(Cont), [Ini|Inv].
-----------------------------------------------^-here

You have accidentally built a deep list, which does not print as a
string (beginner mistake no ~3). If you would have written your deep
list to a file, or printed it with io:format("~s~n", [[$c,[$a,[$b]]]]),
you would have got what you wanted.

And why not use lists:reverse/1,2 instead of writing your own? They use
a built in emulator function that is much faster than any Erlang code.

Background: There are no strings in Erlang. They are represented as
lists of character ascii values. "abc" is syntactical sugar for
[$a,$b,$c]. If you print a list using format character ~p it will use
the "abc" syntactical sugar style if the list is a flat list of
printable characters.

/ Raimo Niskanen, Erlang/OTP



Pablo Dejuan wrote:

> Hi. I'm new in this and I have dificulties using strings. I tried to do
> an "inverese" function that should turn "bac" into "cab" but instead of
> this I get [98,[97,[99,[]]]] which I noticed that are tha Ascii codes
> for c, a, and b.
>
> Here is the code:
> inversa("") -> "";
> inversa([Ini|Cont]) -> Inv= inversa(Cont), [Ini,Inv].
>
> Thanks In Advance.
>



Reply | Threaded
Open this post in threaded view
|

Problems with Strings

Ulf Wiger-4
In reply to this post by Pablo Dejuan

Raimo has given one answer. Here's a suggestion on how to
write your inversa/1 function in order to get the desired
effect. The common trick is to use an accumulator:

-module(demo).
-export([inversa/1]).

inversa(Str) ->
    inversa(Str, []).

inversa([], Acc) ->
    Acc;
inversa([H|T], Acc) ->
    inversa(T, [H|Acc]).



Eshell V5.2.3.1  (abort with ^G)

1> demo:inversa("abc").
"cba"
2>


The accumulator is often useful when writing list iterator
functions (if you don't want to use the map, fold et al in
lists.erl). The thing to bear in mind is that the
accumulator will be reversed at the end of the iteration. In
your case, it's what you want. The most common situation is:

iter(List, F) ->
  iter(List, F, Acc = []).

iter([H|T], F, Acc) ->
   iter(T, F, [F(H)|Acc]);
iter([], F, Acc) ->
   %% return the accumulator, after reversing it.
   lists:reverse(Acc).

Semantically, this is the same as

iter([H|T], F) ->
   [F(H)|iter(T,F)];
iter([], _) ->
   [].

But the former tends to be more efficient, despite the extra
reverse() at the end, because you make more efficient use of
the call stack.


As Raimo pointed out, lists:reverse("abc") is certainly the
most straightforward (and efficient!) way of reversing a
string.

Using lists:foldl/3 is a more contrived way:


2> lists:foldl(fun(Char,Acc) -> [Char|Acc] end, [], "abc").
"cba"

(That is, lists:foldl/3 with a function that only
"saves" each item works exactly like lists:reverse/1)

3> lists:foldr(fun(Char,Acc) -> [Char|Acc] end, [], "abc").
"abc"

(That is, lists:foldr/3 with the same function does not
produce a reversed list.)


The only point of showing this is to point out that there
are many ways to iterate over a list. Some approaches
naturally produce reversed output, while others don't.
The natural way to simply reverse a list is lists:reverse/1.


Hopefully, this didn't just increase your confusion. (:

/Uffe


On Fri, 28 Mar 2003, Pablo Dejuan wrote:

>Hi. I'm new in this and I have dificulties using strings. I
>tried to do an "inverese" function that should turn "bac"
>into "cab" but instead of this I get [98,[97,[99,[]]]]
>which I noticed that are tha Ascii codes for c, a, and b.
>
>Here is the code:
>inversa("") -> "";
>inversa([Ini|Cont]) -> Inv= inversa(Cont), [Ini,Inv].
>
>Thanks In Advance.
>
>

--
Ulf Wiger, Senior Specialist,
   / / /   Architecture & Design of Carrier-Class Software
  / / /    Strategic Product & System Management
 / / /     Ericsson AB, Connectivity and Control Nodes



Reply | Threaded
Open this post in threaded view
|

Problems with Strings

Pablo Dejuan
Dear folks,
Thank you very much for the answers.
I was trying to rewrite that function because it was part of my current
studies.
I just get the idea!
After looking at the API more carefully, I realized that to concat two
strings you should use ++ (I know it's sounds silly but I was confused
with other languages).
So I'll rewrite it as:
inversa("") -> "";
inversa([A1|A]) -> inversa(A) ++ [A1].

See you




Reply | Threaded
Open this post in threaded view
|

Problems with Strings

Ulf Wiger-4
On Fri, 28 Mar 2003, Pablo Dejuan wrote:

>After looking at the API more carefully, I realized that to concat two
>strings you should use ++ (I know it's sounds silly but I was confused
>with other languages).
>So I'll rewrite it as:
>inversa("") -> "";
>inversa([A1|A]) -> inversa(A) ++ [A1].

You could do that, but be aware of the fact that append has
O(N) complexity. I some cases, this may not matter, but it's
always good to know.

Lists are represented as "cons" cells. Look upon them as
linked lists (without a "last" pointer). Using ++ to append
is about as expensive as traversing a normal linked list
from start to end each iteration in order to add one
element.

I assume that what you're after is something other than
simply reversing a string (since you can't beat
lists:reverse/1 for that). If you really want to implement
reverse in plain erlang, then this is the most efficient
version:

reverse(List) -> reverse(List, []).
reverse([H|T],Acc) ->
  reverse(T,[H|Acc]);
reverse([], Acc) ->
  Acc.

A few words then on ++

If you use ++ in hard-coded constructs like

 URL = "http://" ++ Location

this is no more expensive than

 URL = [$h,$t,$t,$p,$:,$/,$/|Location]

but some may find it easier to read.

You may also use ++ in pattern matching:

  location("http://" ++ L) ->
     L.

which is equivalent to

  location([$h,$t,$t,$p,$:,$/,$/|L]) -> L.


These constructs yield identical compiled code.

/Uffe
--
Ulf Wiger, Senior Specialist,
   / / /   Architecture & Design of Carrier-Class Software
  / / /    Strategic Product & System Management
 / / /     Ericsson AB, Connectivity and Control Nodes