Re: Bytes module versus String module.
"Douglas Lewit [email protected] [ocaml_beginners]" <[email protected]> Wed, 8 Feb 2017 09:27:27 -0600
| Newsgroups | gmane.comp.lang.ocaml.beginners |
|---|---|
| Message-ID | <CAM0XMJQU9JPp0N-531JwKNLRQDzTbZiyh6cS6obXo+p1gVmAAA@mail.gmail.com> |
--001a113e7bf690cd6305480680d0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: quoted-printable Hi Gabriel, I appreciate the feedback. A couple questions.... first off, regarding the -safe-string flag.... is that an option only for compiling OCaml code? 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 luckily simpler than in OCaml. In Python for example if I have a list, called lst, 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 of 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 seems that the head/tail analysis of the string should be pretty efficient. So 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 provides 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] [ocaml_beginners] <[email protected]> wrote: > > > String represents (immutable) text, while Bytes represent (mutable) array= s > of bytes. Right now, we are in a transition period where the string 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 types > `string` and `bytes` are compatible (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. > > 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 character > 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. Because > 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 -- preferabl= y > 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 just >> for the sake of experimentation I tried to substitute the "String" modul= e >> 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 differe= nt >> 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 th= e >> 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 are >> 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 > --001a113e7bf690cd6305480680d0 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">Hi Gabriel,</font><div><font siz= e=3D"4"><br></font></div><div><font size=3D"4">I appreciate the feedback.= =C2=A0 A couple questions.... first off, regarding the -safe-string flag...= . is that an option only for compiling OCaml code?=C2=A0 Or can that be ent= ered into the top level ( for example, utop ) ? =C2=A0</font></div><div><fo= nt size=3D"4"><br></font></div><div><font size=3D"4">Yes, I really do enjoy= the recursive treatment of lists.=C2=A0 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, alth= ough in Ruby and Python the syntax is luckily simpler than in OCaml.=C2=A0 = In Python for example if I have a list, called lst, I can do:</font></div><= div><font size=3D"4"><br></font></div><div><font size=3D"4">head =3D lst[0]= </font></div><div><font size=3D"4">tail =C2=A0 =C2=A0=3D lst[1:]</font></di= v><div><font size=3D"4"><br></font></div><div><font size=3D"4">and from the= re I can build 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 a= round with Haskell a little ( although I am by no means a Haskell pro ) and= in that language strings are really lists of chars, so any recursive algor= ithm that you can apply to a list can also be applied to a string because i= n Haskell strings 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 c= hars.=C2=A0 I don't know why I felt the need to match on a string inste= ad, but.... oh well.</font></div><div><font size=3D"4"><br></font></div><di= v><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 t= he string should be pretty efficient.=C2=A0 So for example:</font></div><di= v><font size=3D"4"><br></font></div><div><font size=3D"4">head "Gabrie= l" ---> "G"</font></div><div><font size=3D"4">tail =C2=A0= =C2=A0"Gabriel" ---> "abriel"</font></div><div><fon= t size=3D"4"><br></font></div><div><font size=3D"4">and I keep repeating un= til finally I have:</font></div><div><font size=3D"4"><br></font></div><div= ><font size=3D"4">head "l" =C2=A0---> "l"</font></di= v><div><font size=3D"4">tail =C2=A0 =C2=A0"l" =C2=A0---> =C2= =A0"" =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 manipulation the natural base case should be the empty string, j= ust as the empty list often provides the base case for recursive algorithms= that are implemented on lists.</font></div><div><font size=3D"4"><br></fon= t></div><div><font size=3D"4">Is there a more efficient approach?=C2=A0 I k= now the Str module contains some shortcuts, but I don't like to depend = on too many shortcuts.=C2=A0 It's more fun to design and implement my o= wn algorithms for solving some of these problems, although in a pinch short= cuts are great.</font></div><div><font size=3D"4"><br></font></div><div><fo= nt 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></d= iv><div><font size=3D"4">=C2=A0=C2=A0</font></div></div><div class=3D"gmail= _extra"><br><div class=3D"gmail_quote">On Tue, Feb 7, 2017 at 5:42 PM, Gabr= iel Scherer <a href=3D"mailto:[email protected]">gabriel.scherer@gm= ail.com</a> [ocaml_beginners] <span dir=3D"ltr"><<a href=3D"mailto:ocaml= [email protected]" target=3D"_blank">[email protected]= om</a>></span> wrote:<br><blockquote class=3D"gmail_quote" style=3D"bord= er-left:1px #ccc solid;"> <u></u> =20 <div style=3D"background-color:#fff;"> <span>=C2=A0</span> <div id=3D"m_-7695272532149913180ygrp-mlmsg"> <div id=3D"m_-7695272532149913180ygrp-msg"> <div id=3D"m_-7695272532149913180ygrp-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 class><div clas= s=3D"gmail_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"= >[email protected]</a> [ocaml_beginners] <span dir=3D"ltr"><<a href=3D"m= ailto:[email protected]" target=3D"_blank">ocaml_beginners@ya= hoogroups.<wbr>com</a>></span> wrote:<br><blockquote class=3D"gmail_quot= e" style=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> </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/14760;_ylc=3DX3oDMTJxY2xqbTcwBF9TAzk3MzU5NzE0BGdycElkAzQ5OTkxOTQE= Z3Jwc3BJZAMxNzA1MDA2NzY0BG1zZ0lkAzE0NzYwBHNlYwNmdHIEc2xrA3JwbHkEc3RpbWUDMTQ= 4NjU2ODA3MQ--?act=3Dreply&messageNum=3D14760">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=3DX3oDMTJlZnY2N29yBF9TAzk3MzU5NzE0BGdycElkAzQ5O= TkxOTQEZ3Jwc3BJZAMxNzA1MDA2NzY0BHNlYwNmdHIEc2xrA250cGMEc3RpbWUDMTQ4NjU2ODA3= MQ--" 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=3DX3oDMTM2aWtqMmt2BF9TAzk3M= zU5NzE0BGdycElkAzQ5OTkxOTQEZ3Jwc3BJZAMxNzA1MDA2NzY0BG1zZ0lkAzE0NzYwBHNlYwNm= dHIEc2xrA3Z0cGMEc3RpbWUDMTQ4NjU2ODA3MQR0cGNJZAMxNDc1OA--" style=3D"text-dec= oration: none; color: #2D50FD;">Messages in this topic</a> (3) </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=3DX3oDMTJlcHE4dnM2BF9TAzk3MzU5NzE0BGdycElkAzQ5OTkxOTQEZ3Jwc= 3BJZAMxNzA1MDA2NzY0BHNlYwN2dGwEc2xrA3ZnaHAEc3RpbWUDMTQ4NjU2ODA3MQ--" 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=3DX3oDMTJkdWtnN2szBF9TAzk3ND= c2NTkwBGdycElkAzQ5OTkxOTQEZ3Jwc3BJZAMxNzA1MDA2NzY0BHNlYwNmdHIEc2xrA2dmcARzd= GltZQMxNDg2NTY4MDcx" 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=3D14760/stime=3D1486568071" 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 --> --001a113e7bf690cd6305480680d0--