Re: Bytes module versus String module.
"Douglas Lewit [email protected] [ocaml_beginners]" <[email protected]> Wed, 8 Feb 2017 13:48:44 -0600
| Newsgroups | gmane.comp.lang.ocaml.beginners |
|---|---|
| Message-ID | <CAM0XMJQoRLSO_=g2V_G3O7B1ESE7HKzPCAy634nohGBeR82zdQ@mail.gmail.com> |
--001a113e7bf6057a2305480a2759 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: quoted-printable Thanks Gabriel.... that made a lot of sense. I tried my function on a string containing almost 56, 000 words ( so way more than 56, 000 characters ) and it took the function a couple minutes to process everything. I also tried: *#load "str.cma" ;;* *Str.split ( Str.regexp "[ ]+" ) my_string * and this took less than a second! Your comment was interesting.....* manipulate the indices of the string rather than the string object itself*. Do you think this is normally the best practice for manipulating strings in OCaml? You mentioned that you have students. I hope my question isn't too personal, but I am curious about where you teach. Are you a computer science teacher? Or math teacher? Engineering teacher? How long have you been teaching? Thanks and have a great day. Best, Douglas. On Wed, Feb 8, 2017 at 10:12 AM, Gabriel Scherer [email protected] [ocaml_beginners] <[email protected]> wrote: > > > Because computing the tail takes time and space linear in the size of the > tail, repeatedly computing the tail of a string is quadratic: (tail > "Gabriel") allocates (at least) 6 bytes, (tail "abriel") 5 bytes, (tail > "briel") 4 bytes, etc., in total you allocate (at least) 21 bytes just to > traverse a string of 7 characters (in general it's n*(n-1)/2). On the > contrary, taking the tail of a list returns instantly and allocates nothi= ng > (in OCaml; for Python, you might be in trouble, although it's hard to tel= l > as common operations are optimized, muddying the performance mental model= ). > > You can write code more efficiently without using a more powerful library > (String or other), just using a programming style that is more suited to > lists. My advice to my students (that always start by falling in the same > pitfall as you) is to manipulate *indices* in the string, rather than the > string directly, in their recursive manipulations. > > In the case of your example, I would propose for example the following > structure: > > let remove_spaces str =3D > let len =3D String.length str in > let word start cur =3D > String.sub str start (cur - start) in > let rec skip i acc =3D > ... > and read start cur acc =3D > ... > in > List.rev (skip 0 []) > > where (skip i acc) skips characters that are spaces (it's parameter (i) i= s > the first character to look at), and (read start cur acc) reads a word: > (start) is the position of the first letter already found in the word, > (cur) is the current position to examine (it may be a letter, in which ca= se > the words continue, or a space, in which case the word is complete). (acc= ) > is the accumulator, the list of words found previously (in reverse order)= . > (skip) calls (read) when it finds a non-space; when (read) finds a space, > it adds a word to (acc) and calls (skip). Finally, (word start cur) is > called when (start) is known to be the first non-space character of a wor= d > and (cur) is known to be the first space character after a word, and > returns the word. > > (It is possible to traverse the string in the reverse order, from end to > beginning, to avoid having to reverse the accumulator in the end. But I > find it easier to program in this way; then I can always derive the > reversed version from it if I want to be even more efficient. The reverse= d > version uses a (word) function with different assumptions, (word cur stop= ), > where (cur) is known to be the first space before a word and (stop) the > last position of a character in the word, so its code is a bit different)= . > > > On Wed, Feb 8, 2017 at 4:27 PM, Douglas Lewit [email protected] > [ocaml_beginners] <[email protected]> wrote: > >> >> >> Hi Gabriel, >> >> I appreciate the feedback. A couple questions.... first off, regarding >> the -safe-string flag.... is that an option only for compiling OCaml cod= e? >> Or can that be entered into the top level ( for example, utop ) ? >> >> Yes, I really do enjoy the recursive treatment of lists. OCaml provided >> my first experience with the head/tail paradigm of list-processing, >> although I have found that the same paradigm can be applied to lists in >> Ruby and Python as well, although in Ruby and Python the syntax is lucki= ly >> simpler than in OCaml. In Python for example if I have a list, called l= st, >> I can do: >> >> head =3D lst[0] >> tail =3D lst[1:] >> >> and from there I can build my recursive algorithm. >> >> Also, I've been playing around with Haskell a little ( although I am by >> no means a Haskell pro ) and in that language strings are really lists o= f >> chars, so any recursive algorithm that you can apply to a list can also = be >> applied to a string because in Haskell strings are really lists. >> >> Yes, you're right about matching on chars. I don't know why I felt the >> need to match on a string instead, but.... oh well. >> >> Regarding your first criticism of my function.... I'm not sure I follow >> you on that one. Let's say you have a string called "Gabriel". It seem= s >> that the head/tail analysis of the string should be pretty efficient. S= o >> for example: >> >> head "Gabriel" ---> "G" >> tail "Gabriel" ---> "abriel" >> >> and I keep repeating until finally I have: >> >> head "l" ---> "l" >> tail "l" ---> "" ( the empty string ) >> >> It seems logical that for the sake of string manipulation the natural >> base case should be the empty string, just as the empty list often provi= des >> the base case for recursive algorithms that are implemented on lists. >> >> Is there a more efficient approach? I know the Str module contains some >> shortcuts, but I don't like to depend on too many shortcuts. It's more = fun >> to design and implement my own algorithms for solving some of these >> problems, although in a pinch shortcuts are great. >> >> Best, >> >> Douglas. >> >> >> >> On Tue, Feb 7, 2017 at 5:42 PM, Gabriel Scherer [email protected]= m >> [ocaml_beginners] <[email protected]> wrote: >> >>> >>> >>> String represents (immutable) text, while Bytes represent (mutable) >>> arrays of bytes. Right now, we are in a transition period where the str= ing >>> type played both roles, and in particular was always mutable (which is = not >>> a very good idea): by default, during this transition period, the two t= ypes >>> `string` and `bytes` are compatible (you can use functions of one on th= e >>> other). If you enable the `-safe-string` flag, then the two types will = be >>> considered incompatible; using it in your code is good practice, and it >>> will become the default in a future OCaml version. >>> >>> Regarding your code: >>> - repeatedly calling String.sub is inefficient; you want to count the >>> number of spaces at the beginning and call String.sub just once >>> - there is no need to use String.make here, you can match on a characte= r >>> directly ('c' instead of "c") >>> >>> Your code is relatively typical of people that enjoy recursively >>> processing lists, and try to apply the same approach to strings. Becaus= e >>> the "tail" operation on strings takes linear (O(n)) instead of constant >>> (O(1)) time, this is not a very good idea performance-wise. Reading >>> characters at any position is fine, but the rest (String.sub, string >>> concatenation (^)...) should be used as sparsely as possible -- prefera= bly >>> not at each recursive call. >>> >>> On Tue, Feb 7, 2017 at 11:35 PM, Douglas Lewit [email protected] >>> [ocaml_beginners] <[email protected]> wrote: >>> >>>> >>>> >>>> Hi everyone, >>>> >>>> I wrote the following functions. >>>> >>>> >>>> *(* The function removeSpacesFromString' relies on the function >>>> removeSpacesFromString. removeSpacesFromString' takes a string * >>>> * such as "How are you doing" and parses it into a list of separate >>>> words. In this case for example, removeSpacesFromString' * >>>> * will return ["How"; "are"; "you"; "doing"]. *)* >>>> >>>> >>>> *let rec removeSpacesFromString s w l =3D match Bytes.length s with * >>>> * |0 -> List.rev begin [w= ] >>>> :: l end* >>>> * |_ -> match Bytes.make = 1 >>>> ( Bytes.get s 0 ) with * >>>> * |" " -> >>>> removeSpacesFromString ( Bytes.sub s 1 ( Bytes.length s - 1 ) ) "" *= *([w] >>>> :: l)* >>>> * |_ -> >>>> removeSpacesFromString ( Bytes.sub s 1 ( Bytes.length s - 1 ) ) * >>>> * begin = w >>>> ^ ( Bytes.make 1 ( Bytes.get s 0 ) ) end l ;;* >>>> >>>> >>>> *let removeSpacesFromString' s =3D List.flatten begin >>>> removeSpacesFromString s "" [] end ;;* >>>> >>>> >>>> The functions work exactly as I want them to, which is great! But jus= t >>>> for the sake of experimentation I tried to substitute the "String" mod= ule >>>> for the "Bytes" module into the first function above. Well guess what= ? >>>> Nothing really changed! I still get the exact same results as before, >>>> which leads to an interesting question. How is the Bytes module diffe= rent >>>> from the String module? I sort of get the impression from the online >>>> documentation that the Bytes module is the "new and improved" version = of >>>> the String module, but if that's the case then why doesn't INRIA just >>>> delete the String module from OCaml's Standard Library? Are the two >>>> libraries pretty much the same? How is the Bytes library better than = the >>>> String library? What would happen if INRIA just decided to remove the >>>> String library from the next major version of OCaml? >>>> >>>> One quick question here before I send my email. How much does OCaml >>>> depend on C? I know that Python and Ruby are essentially very large "= C >>>> applications" in the sense that the interpreters for those languages a= re >>>> coded in the C language. ( Or at least that's what I have read online= . ) >>>> What about OCaml. How is OCaml's compiler implemented? Is the OCaml >>>> compiler written in C? Or written in something else? >>>> >>>> I appreciate your feedback and thanks! >>>> >>>> Best, >>>> >>>> Douglas Lewit >>>> >>>> >>>> >>>> >>> >> >> >> >=20 > --001a113e7bf6057a2305480a2759 Content-Type: text/html; charset=UTF-8 Content-Transfer-Encoding: quoted-printable <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/htm= l4/strict.dtd"> <html> <head> </head> =20 <body style=3D"background-color: #fff;"> <span style=3D"display:none"> </span> <!--~-|**|PrettyHtmlStartT|**|-~--> <div id=3D"ygrp-mlmsg" style=3D"position:relative;"> <div id=3D"ygrp-msg" style=3D"z-index: 1;"> <!--~-|**|PrettyHtmlEndT|**|-~--> <div id=3D"ygrp-text" > =20=20=20=20=20=20 =20=20=20=20=20=20 <p><div dir=3D"ltr"><font size=3D"4">Thanks Gabriel.... that made a l= ot of sense.=C2=A0 I tried my function on a string containing almost 56, 00= 0 words ( so way more than 56, 000 characters ) and it took the function a = couple minutes to process everything.=C2=A0 I also tried:</font><div><font = size=3D"4"><br></font></div><div><font size=3D"4"><b>#load "str.cma&qu= ot; ;;</b></font></div><div><font size=3D"4"><b><br></b></font></div><div><= font size=3D"4"><b>Str.split ( Str.regexp "[ ]+" ) my_string= =C2=A0</b></font></div><div><font size=3D"4"><br></font></div><div><font si= ze=3D"4">and this took less than a second!=C2=A0</font></div><div><font siz= e=3D"4"><br></font></div><div><font size=3D"4">Your comment was interesting= .....<i> manipulate the indices of the string rather than the string object= itself</i>.=C2=A0 Do you think this is normally the best practice for mani= pulating strings in OCaml?</font></div><div><font size=3D"4"><br></font></d= iv><div><font size=3D"4">You mentioned that you have students.=C2=A0 I hope= my question isn't too personal, but I am curious about where you teach= .=C2=A0 Are you a computer science teacher?=C2=A0 Or math teacher?=C2=A0 En= gineering teacher?=C2=A0 How long have you been teaching? =C2=A0</font></di= v><div><font size=3D"4"><br></font></div><div><font size=3D"4">Thanks and h= ave a great day.</font></div><div><font size=3D"4"><br></font></div><div><f= ont size=3D"4">Best,</font></div><div><font size=3D"4"><br></font></div><di= v><font size=3D"4">Douglas.</font></div><div><font size=3D"4">=C2=A0=C2=A0<= /font></div></div><div class=3D"gmail_extra"><br><div class=3D"gmail_quote"= >On Wed, Feb 8, 2017 at 10:12 AM, Gabriel Scherer <a href=3D"mailto:gabriel= [email protected]">[email protected]</a> [ocaml_beginners] <span d= ir=3D"ltr"><<a href=3D"mailto:[email protected]" target=3D= "_blank">[email protected]</a>></span> wrote:<br><blockquo= te class=3D"gmail_quote" style=3D"border-left:1px #ccc solid;"> <u></u> =20 <div style=3D"background-color:#fff;"> <span>=C2=A0</span> <div id=3D"m_4179827589697787686ygrp-mlmsg"> <div id=3D"m_4179827589697787686ygrp-msg"> <div id=3D"m_4179827589697787686ygrp-text"> =20=20=20=20=20=20 =20=20=20=20=20=20 <p></p><div dir=3D"ltr"><div><div><div>Because computing the tail tak= es time and space linear in the size of the tail, repeatedly computing the = tail of a string is quadratic: (tail "Gabriel") allocates (at lea= st) 6 bytes, (tail "abriel") 5 bytes, (tail "briel") 4 = bytes, etc., in total you allocate (at least) 21 bytes just to traverse a s= tring of 7 characters (in general it's n*(n-1)/2). On the contrary, tak= ing the tail of a list returns instantly and allocates nothing (in OCaml; f= or Python, you might be in trouble, although it's hard to tell as commo= n operations are optimized, muddying the performance mental model).<br><br>= </div>You can write code more efficiently without using a more powerful lib= rary (String or other), just using a programming style that is more suited = to lists. My advice to my students (that always start by falling in the sam= e pitfall as you) is to manipulate *indices* in the string, rather than the= string directly, in their recursive manipulations.<br><br>In the case of y= our example, I would propose for example the following structure:<br><br>l= et remove_spaces str =3D<br>=C2=A0 let len =3D String.length str in<br>=C2= =A0 let word start cur =3D<br>=C2=A0=C2=A0=C2=A0 String.sub str start (cur = - start) in<br>=C2=A0 let rec skip i acc =3D<br>=C2=A0=C2=A0=C2=A0 ...<br>= =C2=A0 and read start cur acc =3D<br>=C2=A0=C2=A0=C2=A0 ...<br>=C2=A0 in<br= >=C2=A0 List.rev (skip 0 [])<br><br></div><div>where (skip i acc) skips ch= aracters that are spaces (it's parameter (i) is the first character to = look at), and (read start cur acc) reads a word: (start) is the position of= the first letter already found in the word, (cur) is the current position = to examine (it may be a letter, in which case the words continue, or a spac= e, in which case the word is complete). (acc) is the accumulator, the list = of words found previously (in reverse order). (skip) calls (read) when it f= inds a non-space; when (read) finds a space, it adds a word to (acc) and ca= lls (skip). Finally, (word start cur) is called when (start) is known to be= the first non-space character of a word and (cur) is known to be the first= space character after a word, and returns the word.<br><br></div><div>(It = is possible to traverse the string in the reverse order, from end to beginn= ing, to avoid having to reverse the accumulator in the end. But I find it e= asier to program in this way; then I can always derive the reversed version= from it if I want to be even more efficient. The reversed version uses a (= word) function with different assumptions, (word cur stop), where (cur) is = known to be the first space before a word and=C2=A0 (stop) the last positio= n of a character in the word, so its code is a bit different).<br></div><di= v><br></div></div></div><div><div class=3D"h5"><div class=3D"gmail_extra"><= br><div class=3D"gmail_quote">On Wed, Feb 8, 2017 at 4:27 PM, Douglas Lewit= <a href=3D"mailto:[email protected]" target=3D"_blank">[email protected]</= a> [ocaml_beginners] <span dir=3D"ltr"><<a href=3D"mailto:ocaml_beginner= [email protected]" target=3D"_blank">ocaml_beginners@yahoogroups.<wbr>com</= a>></span> wrote:<br><blockquote class=3D"gmail_quote" style=3D"border-l= eft:1px #ccc solid;"> =20=20=20=20=20=20=20=20 <div> <br><br> <div dir=3D"ltr"><font size=3D"4">Hi Gabriel,</font><div><font size=3D"4"><= br></font></div><div><font size=3D"4">I appreciate the feedback.=C2=A0 A co= uple questions.... first off, regarding the -safe-string flag.... is that a= n option only for compiling OCaml code?=C2=A0 Or can that be entered into t= he top level ( for example, utop ) ? =C2=A0</font></div><div><font size=3D"= 4"><br></font></div><div><font size=3D"4">Yes, I really do enjoy the recurs= ive treatment of lists.=C2=A0 OCaml provided my first experience with the h= ead/tail paradigm of list-processing, although I have found that the same p= aradigm can be applied to lists in Ruby and Python as well, although in Rub= y and Python the syntax is luckily simpler than in OCaml.=C2=A0 In Python f= or example if I have a list, called lst, I can do:</font></div><div><font s= ize=3D"4"><br></font></div><div><font size=3D"4">head =3D lst[0]</font></di= v><div><font size=3D"4">tail =C2=A0 =C2=A0=3D lst[1:]</font></div><div><fon= t size=3D"4"><br></font></div><div><font size=3D"4">and from there I can bu= ild my recursive algorithm. =C2=A0</font></div><div><font size=3D"4"><br></= font></div><div><font size=3D"4">Also, I've been playing around with Ha= skell a little ( although I am by no means a Haskell pro ) and in that lang= uage strings are really lists of chars, so any recursive algorithm that you= can apply to a list can also be applied to a string because in Haskell str= ings are really lists.</font></div><div><font size=3D"4"><br></font></div><= div><font size=3D"4">Yes, you're right about matching on chars.=C2=A0 I= don't know why I felt the need to match on a string instead, but.... o= h well.</font></div><div><font size=3D"4"><br></font></div><div><font size= =3D"4">Regarding your first criticism of my function.... I'm not sure I= follow you on that one.=C2=A0 Let's say you have a string called "= ;Gabriel".=C2=A0 It seems that the head/tail analysis of the string sh= ould be pretty efficient.=C2=A0 So for example:</font></div><div><font size= =3D"4"><br></font></div><div><font size=3D"4">head "Gabriel" ---&= gt; "G"</font></div><div><font size=3D"4">tail =C2=A0 =C2=A0"= ;Gabriel" ---> "abriel"</font></div><div><font size=3D"4"= ><br></font></div><div><font size=3D"4">and I keep repeating until finally = I have:</font></div><div><font size=3D"4"><br></font></div><div><font size= =3D"4">head "l" =C2=A0---> "l"</font></div><div><fon= t size=3D"4">tail =C2=A0 =C2=A0"l" =C2=A0---> =C2=A0"&quo= t; =C2=A0( the empty string )</font></div><div><font size=3D"4"><br></font>= </div><div><font size=3D"4">It seems logical that for the sake of string ma= nipulation the natural base case should be the empty string, just as the em= pty list often provides the base case for recursive algorithms that are imp= lemented on lists.</font></div><div><font size=3D"4"><br></font></div><div>= <font size=3D"4">Is there a more efficient approach?=C2=A0 I know the Str m= odule contains some shortcuts, but I don't like to depend on too many s= hortcuts.=C2=A0 It's more fun to design and implement my own algorithms= for solving some of these problems, although in a pinch shortcuts are grea= t.</font></div><div><font size=3D"4"><br></font></div><div><font size=3D"4"= >Best,</font></div><div><font size=3D"4"><br></font></div><div><font size= =3D"4">Douglas.</font></div><div><font size=3D"4"><br></font></div><div><fo= nt size=3D"4">=C2=A0=C2=A0</font></div></div><div><div class=3D"m_417982758= 9697787686h5"><div class=3D"gmail_extra"><br><div class=3D"gmail_quote">On = Tue, Feb 7, 2017 at 5:42 PM, Gabriel Scherer <a href=3D"mailto:gabriel.sche= [email protected]" target=3D"_blank">[email protected]</a> [ocaml_begin= ners] <span dir=3D"ltr"><<a href=3D"mailto:[email protected]= om" target=3D"_blank">[email protected]<wbr>om</a>></span> w= rote:<br><blockquote class=3D"gmail_quote" style=3D"border-left:1px #ccc so= lid;"> <u></u> =20 <div style=3D"background-color:#fff;"> <span>=C2=A0</span> <div id=3D"m_4179827589697787686m_5840563030612842395m_-7695272532149913180= ygrp-mlmsg"> <div id=3D"m_4179827589697787686m_5840563030612842395m_-76952725321499131= 80ygrp-msg"> <div id=3D"m_4179827589697787686m_5840563030612842395m_-769527253214991= 3180ygrp-text"> =20=20=20=20=20=20 =20=20=20=20=20=20 <p></p><div dir=3D"ltr"><div><div><div><div>String represents (immuta= ble) text, while Bytes represent (mutable) arrays of bytes. Right now, we a= re in a transition period where the string type played both roles, and in p= articular was always mutable (which is not a very good idea): by default, = during this transition period, the two types `string` and `bytes` are compa= tible (you can use functions of one on the other). If you enable the `-safe= -string` flag, then the two types will be considered incompatible; using it= in your code is good practice, and it will become the default in a future = OCaml version.<br><br></div>Regarding your code:<br></div>- repeatedly call= ing String.sub is inefficient; you want to count the number of spaces at th= e beginning and call String.sub just once<br></div>- there is no need to us= e String.make here, you can match on a character directly ('c' inst= ead of "c")<br><br></div>Your code is relatively typical of peopl= e that enjoy recursively processing lists, and try to apply the same approa= ch to strings. Because the "tail" operation on strings takes line= ar (O(n)) instead of constant (O(1)) time, this is not a very good idea per= formance-wise. Reading characters at any position is fine, but the rest (St= ring.sub, string concatenation (^)...) should be used as sparsely as possib= le -- preferably not at each recursive call.<br></div><span><div class=3D"g= mail_extra"><br><div class=3D"gmail_quote">On Tue, Feb 7, 2017 at 11:35 PM,= Douglas Lewit <a href=3D"mailto:[email protected]" target=3D"_blank">delew= [email protected]</a> [ocaml_beginners] <span dir=3D"ltr"><<a href=3D"mailto:= [email protected]" target=3D"_blank">ocaml_beginners@yahoogro= ups.c<wbr>om</a>></span> wrote:<br><blockquote class=3D"gmail_quote" sty= le=3D"border-left:1px #ccc solid;"> =20=20=20=20=20=20=20=20 <div> <br><br> <div dir=3D"ltr"><font size=3D"4">Hi everyone,</font><div><font size=3D"4">= <br></font></div><div><font size=3D"4">I wrote the following functions.</fo= nt></div><div><font size=3D"4"><br></font></div><div><font size=3D"4" color= =3D"#cc0000"><b><br></b></font></div><div><font size=3D"4"><div><b><font co= lor=3D"#20124d">(* The function removeSpacesFromString' relies on the f= unction removeSpacesFromString. =C2=A0removeSpacesFromString' takes a s= tring=C2=A0</font></b></div><div><b><font color=3D"#20124d">=C2=A0 =C2=A0su= ch as "How are you doing" and parses it into a list of separate w= ords.=C2=A0 In this case for example, removeSpacesFromString'=C2=A0</fo= nt></b></div><div><b><font color=3D"#20124d">=C2=A0 =C2=A0will return [&quo= t;How"; "are"; "you"; "doing"]. *)</font= ></b></div><div><b><font color=3D"#20124d"><br></font></b></div><div><b><fo= nt color=3D"#20124d"><br></font></b></div><div><b><font color=3D"#20124d">l= et rec removeSpacesFromString s w l =3D match Bytes.length s with=C2=A0</fo= nt></b></div><div><b><font color=3D"#20124d">=C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 |0 -> List.r= ev begin [w] :: l end</font></b></div><div><b><font color=3D"#20124d">=C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 |_ -> match Bytes.make 1 ( Bytes.get s 0 ) with=C2=A0</font><= /b></div><div><b><font color=3D"#20124d">=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0= =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 |= " " -> removeSpacesFromString ( Bytes.sub s 1 ( Bytes.length s= - 1 ) ) =C2=A0"" =C2=A0</font></b><b><font color=3D"#20124d">([w= ] :: l)</font></b></div><div><b><font color=3D"#20124d">=C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 |_ =C2=A0 -> removeSpacesFromString ( Bytes.sub s 1 ( = Bytes.length s - 1 ) )=C2=A0</font></b></div><div><b><font color=3D"#20124d= ">=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0begin w ^ ( Bytes.make 1 ( Bytes.get s 0 ) ) =C2=A0end l ;;</font></b></= div><div><b><font color=3D"#20124d"><br></font></b></div><div><b><font colo= r=3D"#20124d"><br></font></b></div><div><b><font color=3D"#20124d">let remo= veSpacesFromString' s =3D List.flatten begin removeSpacesFromString s &= quot;" [] end ;;</font></b></div><div><b><font color=3D"#cc0000"><br><= /font></b></div><div><br></div><div>The functions work exactly as I want th= em to, which is great!=C2=A0 But just for the sake of experimentation I tri= ed to substitute the "String" module for the "Bytes" mo= dule into the first function above.=C2=A0 Well guess what?=C2=A0 Nothing re= ally changed!=C2=A0 I still get the exact same results as before, which lea= ds to an interesting question.=C2=A0 How is the Bytes module different from= the String module?=C2=A0 I sort of get the impression from the online docu= mentation that the Bytes module is the "new and improved" version= of the String module, but if that's the case then why doesn't INRI= A just delete the String module from OCaml's Standard Library?=C2=A0 Ar= e the two libraries pretty much the same?=C2=A0 How is the Bytes library be= tter than the String library?=C2=A0 What would happen if INRIA just decided= to remove the String library from the next major version of OCaml?</div><d= iv><br></div><div>One quick question here before I send my email.=C2=A0 How= much does OCaml depend on C?=C2=A0 I know that Python and Ruby are essenti= ally very large "C applications" in the sense that the interprete= rs for those languages are coded in the C language. =C2=A0( Or at least tha= t's what I have read online. ) =C2=A0What about OCaml.=C2=A0 How is OCa= ml's compiler implemented?=C2=A0 Is the OCaml compiler written in C?=C2= =A0 Or written in something else?</div><div><br></div><div>I appreciate you= r feedback and thanks!</div><div><br></div><div>Best,</div><div><br></div><= div>Douglas Lewit</div><div><br></div></font></div></div> <br> <br> <div width=3D"1" style=3D"color:white;"></div> </div></blockquote></div><br></div> </span><p></p> </div> =20=20=20=20=20 =20=20=20=20 <div style=3D"color:#fff;height:0;"></div> </div> =20=20 </div></div></blockquote></div><br></div> <br> <br> <div width=3D"1" style=3D"color:white;"></div> </div></div></div></blockquote></div><br></div> </div></div><p></p> </div><div><div class=3D"h5"> =20=20=20=20=20 =20=20=20=20 <div style=3D"color:#fff;height:0;"></div> </div> =20=20 </div></div></div></div></blockquote></div><br></div> </p> </div> =20=20=20=20=20 <!--~-|**|PrettyHtmlStart|**|-~--> <div style=3D"color: #fff; height: 0;">__._,_.___</div> =20=20=20=20=20=20=20=20=20=20 =20=20 =20 =20=20=20=20 <div style=3D"clear:both"> </div> <div id=3D"fromDMARC" style=3D"margin-top: 10px;"> <hr style=3D"height:2px ; border-width:0; color:#E3E3E3; background= -color:#E3E3E3;"> Posted by: Douglas Lewit <[email protected]> <hr style= =3D"height:2px ; border-width:0; color:#E3E3E3; background-color:#E3E3E3;"> </div> <div style=3D"clear:both"> </div> <table cellspacing=3D4px style=3D"margin-top: 10px; margin-bottom: 10px= ; color: #2D50FD;"> <tbody> <tr> <td style=3D"font-size: 12px; font-family: arial; font-weight: bo= ld; padding: 7px 5px 5px;" > <a style=3D"text-decoration: none; color: #2D50FD= " href=3D"https://groups.yahoo.com/neo/groups/ocaml_beginners/conversations= /messages/14763;_ylc=3DX3oDMTJxYWdoZGc0BF9TAzk3MzU5NzE0BGdycElkAzQ5OTkxOTQE= Z3Jwc3BJZAMxNzA1MDA2NzY0BG1zZ0lkAzE0NzYzBHNlYwNmdHIEc2xrA3JwbHkEc3RpbWUDMTQ= 4NjU4MzMyNg--?act=3Dreply&messageNum=3D14763">Reply via web post</a> </td> <td>•</td> <td style=3D"font-size: 12px; font-family: arial; padding: 7px 5p= x 5px;" > <a href=3D"mailto:[email protected]?subject=3DRe%3A%20%22ocaml_= beginners%22%3A%3A%5B%5D%20Bytes%20module%20versus%20String%20module%2E" st= yle=3D"text-decoration: none; color: #2D50FD;"> Reply to sender </a> </td> <td>•</td> <td style=3D"font-size: 12px; font-family: arial; padding: 7px 5p= x 5px;"> <a href=3D"mailto:[email protected]?subject=3DRe%= 3A%20%22ocaml_beginners%22%3A%3A%5B%5D%20Bytes%20module%20versus%20String%2= 0module%2E" style=3D"text-decoration: none; color: #2D50FD"> Reply to group </a> </td> <td>•</td> <td style=3D"font-size: 12px; font-family: arial; padding: 7px 5p= x 5px;" > <a href=3D"https://groups.yahoo.com/neo/groups/ocaml_beginners/= conversations/newtopic;_ylc=3DX3oDMTJlcmxhMnE0BF9TAzk3MzU5NzE0BGdycElkAzQ5O= TkxOTQEZ3Jwc3BJZAMxNzA1MDA2NzY0BHNlYwNmdHIEc2xrA250cGMEc3RpbWUDMTQ4NjU4MzMy= Ng--" style=3D"text-decoration: none; color: #2D50FD">Start a New Topic</a> </td> <td>•</td> <td style=3D"font-size: 12px; font-family: arial; padding: 7px 5p= x 5px;color: #2D50FD;" > <a href=3D"https://groups.yahoo.com/neo/groups/= ocaml_beginners/conversations/topics/14758;_ylc=3DX3oDMTM2ajcxYzlzBF9TAzk3M= zU5NzE0BGdycElkAzQ5OTkxOTQEZ3Jwc3BJZAMxNzA1MDA2NzY0BG1zZ0lkAzE0NzYzBHNlYwNm= dHIEc2xrA3Z0cGMEc3RpbWUDMTQ4NjU4MzMyNgR0cGNJZAMxNDc1OA--" style=3D"text-dec= oration: none; color: #2D50FD;">Messages in this topic</a> (6) </td> </tr> </tbody> </table> =20=20=20=20=20=20=20=20 <div id=3D"megaphoneModule"> <hr style=3D"height:2px ; border-width:0; color:#E3E3E3; backgr= ound-color:#E3E3E3;"> <div> <div class=3D"stream" style=3D"margin-bottom:10px;"> <div style=3D"background-color:white;"> <div class=3D"sn-img" style=3D"display:inline;"><img name=3D"tn= _file" style=3D"padding:0px 10px;vertical-align:top;margin-top:5px;" src=3D= "https://s.yimg.com/ru/static/images/yg/img/megaphone/1464031581_phpFA8bON"= height=3D"82" width=3D"82"></div> <div class=3D"mod-txt" style=3D"display:inline-block;"> <a rel=3D"nofollow" name=3D"sub_url" target=3D"_blank" href= =3D"https://yho.com/1wwmgg" style=3D"color:#0000FF;display:block;margin-lef= t:5px;text-decoration:none;"><span style=3D"font-size:15px;">Have you tried= the highest rated email app?</span></a> <div style=3D"max-width:530px;padding:2px 5px;">With 4.5 st= ars in iTunes, the Yahoo Mail app is the highest rated email app on the mar= ket. What are you waiting for? Now you can access all your inboxes (Gmail, = Outlook, AOL and more) in one place. Never delete an email again with 1000G= B of free cloud storage.</div> </div> </div> </div> </div>=20=20 =20=20=20=20=20 <hr style=3D"height:2px ; border-width:0; color:#E3E3E3; background-col= or:#E3E3E3;"> </div> <!------- Start Nav Bar ------> <div id=3D"ygrp-grfd" style=3D"font-family: Verdana; font-size: 12px; p= adding: 15px 0;"> =20=20=20=20=20=20 <!-- |**|begin egp html banner|**| --> Archives up to December 31, 2011 are also downloadable at <a href=3D"= http://www.connettivo.net/cntprojects/ocaml_beginners">http://www.connettiv= o.net/cntprojects/ocaml_beginners</a><BR> The archives of the very official ocaml list (the seniors' one) can be foun= d at <a href=3D"http://caml.inria.fr">http://caml.inria.fr</a><BR> Attachments are banned and you're asked to be polite, avoid flames etc. = =20=20 <!-- |**|end egp html banner|**| --> </div> =20=20 =20 <!-- |**|begin egp html banner|**| --> <div id=3D"ygrp-vital" style=3D"background-color: #f2f2f2; font-family: Ver= dana; font-size: 10px; margin-bottom: 10px; padding: 10px;"> <span id=3D"vithd" style=3D"font-weight: bold; color: #333; text-transf= orm: uppercase; "><a href=3D"https://groups.yahoo.com/neo/groups/ocaml_begi= nners/info;_ylc=3DX3oDMTJlcWE1ZmhrBF9TAzk3MzU5NzE0BGdycElkAzQ5OTkxOTQEZ3Jwc= 3BJZAMxNzA1MDA2NzY0BHNlYwN2dGwEc2xrA3ZnaHAEc3RpbWUDMTQ4NjU4MzMyNg--" style= =3D"text-decoration: none;">Visit Your Group</a></span> <ul style=3D"list-style-type: none; margin: 0; padding: 0; display: in= line;"> </ul> </div> <div id=3D"ft" style=3D"font-family: Arial; font-size: 11px; margin-top: 5p= x; padding: 0 2px 0 0; clear: both;"> <a href=3D"https://groups.yahoo.com/neo;_ylc=3DX3oDMTJkNjlhYmtnBF9TAzk3ND= c2NTkwBGdycElkAzQ5OTkxOTQEZ3Jwc3BJZAMxNzA1MDA2NzY0BHNlYwNmdHIEc2xrA2dmcARzd= GltZQMxNDg2NTgzMzI2" style=3D"float: left;"><img src=3D"http://l.yimg.com/r= u/static/images/yg/img/email/new_logo/logo-groups-137x15.png" height=3D"15"= width=3D"137" alt=3D"Yahoo! Groups" style=3D"border: 0;"/></a> <div style=3D"color: #747575; float: right;"> • <a href=3D"https://i= nfo.yahoo.com/privacy/us/yahoo/groups/details.html" style=3D"text-decoratio= n: none;">Privacy</a> • <a href=3D"mailto:ocaml_beginners-unsubscribe@= yahoogroups.com?subject=3DUnsubscribe" style=3D"text-decoration: none;">Uns= ubscribe</a> • <a href=3D"https://info.yahoo.com/legal/us/yahoo/utos/t= erms/" style=3D"text-decoration: none;">Terms of Use</a> </div> </div> <br> <!-- |**|end egp html banner|**| --> </div> <!-- ygrp-msg --> =20 <!-- Sponsor --> <!-- |**|begin egp html banner|**| --> <div id=3D"ygrp-sponsor" style=3D"width:160px; float:right; clear:none; m= argin:0 0 25px 0; background: #fff;"> <!-- Start Recommendations --> <div id=3D"ygrp-reco"> </div> <!-- End Recommendations --> </div> <!-- |**|end egp html banner|**| --> <div style=3D"clear:both; color: #FFF; font-size:1px;">.</div> </div> <img src=3D"http://geo.yahoo.com/serv?s=3D97359714/grpId=3D4999194/grpspI= d=3D1705006764/msgId=3D14763/stime=3D1486583326" width=3D"1" height=3D"1"> = <br> <img src=3D"http://y.analytics.yahoo.com/fpc.pl?ywarid=3D515FB27823A7407E&a= =3D10001310322279&js=3Dno&resp=3Dimg&cf12=3DCP" width=3D"1" height=3D"1">=20 <div style=3D"color: #fff; height: 0;">__,_._,___</div> <!--~-|**|PrettyHtmlEnd|**|-~--> </body> <!--~-|**|PrettyHtmlStart|**|-~--> <head> <style type=3D"text/css"> <!-- #ygrp-mkp { border: 1px solid #d8d8d8; font-family: Arial; margin: 10px 0; padding: 0 10px; } #ygrp-mkp hr { border: 1px solid #d8d8d8; } #ygrp-mkp #hd { color: #628c2a; font-size: 85%; font-weight: 700; line-height: 122%; margin: 10px 0; } #ygrp-mkp #ads { margin-bottom: 10px; } #ygrp-mkp .ad { padding: 0 0; } #ygrp-mkp .ad p { margin: 0; } #ygrp-mkp .ad a { color: #0000ff; text-decoration: none; } #ygrp-sponsor #ygrp-lc { font-family: Arial; } #ygrp-sponsor #ygrp-lc #hd { margin: 10px 0px; font-weight: 700; font-size: 78%; line-height: 122%; } #ygrp-sponsor #ygrp-lc .ad { margin-bottom: 10px; padding: 0 0; } #actions { font-family: Verdana; font-size: 11px; padding: 10px 0; } #activity { background-color: #e0ecee; float: left; font-family: Verdana; font-size: 10px; padding: 10px; } #activity span { font-weight: 700; } #activity span:first-child { text-transform: uppercase; } #activity span a { color: #5085b6; text-decoration: none; } #activity span span { color: #ff7900; } #activity span .underline { text-decoration: underline; } .attach { clear: both; display: table; font-family: Arial; font-size: 12px; padding: 10px 0; width: 400px; } .attach div a { text-decoration: none; } .attach img { border: none; padding-right: 5px; } .attach label { display: block; margin-bottom: 5px; } .attach label a { text-decoration: none; } =20=20 blockquote { margin: 0 0 0 4px; } .bold { font-family: Arial; font-size: 13px; font-weight: 700; } .bold a { text-decoration: none; } dd.last p a { font-family: Verdana; font-weight: 700; } dd.last p span { margin-right: 10px; font-family: Verdana; font-weight: 700; } dd.last p span.yshortcuts { margin-right: 0; } div.attach-table div div a { text-decoration: none; } div.attach-table { width: 400px; } div.file-title a, div.file-title a:active, div.file-title a:hover, div.fi= le-title a:visited { text-decoration: none; } div.photo-title a, div.photo-title a:active, div.photo-title a:hover, div= .photo-title a:visited { text-decoration: none; } div#ygrp-mlmsg #ygrp-msg p a span.yshortcuts { font-family: Verdana; font-size: 10px; font-weight: normal; } .green { color: #628c2a; } .MsoNormal { margin: 0 0 0 0; } o { font-size: 0; } #photos div { float: left; width: 72px; } #photos div div { border: 1px solid #666666; height: 62px; overflow: hidden; width: 62px; } #photos div label { color: #666666; font-size: 10px; overflow: hidden; text-align: center; white-space: nowrap; width: 64px; } #reco-category { font-size: 77%; } #reco-desc { font-size: 77%; } .replbq { margin: 4px; } #ygrp-actbar div a:first-child { /* border-right: 0px solid #000;*/ margin-right: 2px; padding-right: 5px; } #ygrp-mlmsg { font-size: 13px; font-family: Arial, helvetica,clean, sans-serif; *font-size: small; *font: x-small; } #ygrp-mlmsg table { font-size: inherit; font: 100%; } #ygrp-mlmsg select, input, textarea { font: 99% Arial, Helvetica, clean, sans-serif; } #ygrp-mlmsg pre, code { font:115% monospace; *font-size:100%; } #ygrp-mlmsg * { line-height: 1.22em; } #ygrp-mlmsg #logo { padding-bottom: 10px; } #ygrp-msg p a { font-family: Verdana; } #ygrp-msg p#attach-count span { color: #1E66AE; font-weight: 700; } #ygrp-reco #reco-head { color: #ff7900; font-weight: 700; } #ygrp-reco { margin-bottom: 20px; padding: 0px; } #ygrp-sponsor #ov li a { font-size: 130%; text-decoration: none; } #ygrp-sponsor #ov li { font-size: 77%; list-style-type: square; padding: 6px 0; }=20 #ygrp-sponsor #ov ul { margin: 0; padding: 0 0 0 8px; } #ygrp-text { font-family: Georgia; } #ygrp-text p { margin: 0 0 1em 0; } #ygrp-text tt { font-size: 120%; } #ygrp-vital ul li:last-child { border-right: none !important;=20 }=20 --> </style> </head> <!--~-|**|PrettyHtmlEnd|**|-~--> </html> <!-- end group email --> --001a113e7bf6057a2305480a2759--