newLISP你也行 --- 字符串

  #############################################################################
  # Name:         newLISP你也行 ---
  # Author:       黃登(winger)
  # Project:      http://code.google.com/p/newlisp-you-can-do
  # Gtalk:        free.winger@gmail.com
  # Gtalk-Group:  zen0code@appspot.com
  # Blog:         http://my.opera.com/freewinger/blog/
  # QQ-Group:     31138659
  # 大道至簡 -- newLISP
  #
  # Copyright 2012 黃登(winger) All rights reserved.
  # Permission is granted to copy, distribute and/or
  # modify this document under the terms of the GNU Free Documentation License,
  # Version 1.2 or any later version published by the Free Software Foundation;
  # with no Invariant Sections, no Front-Cover Texts,and no Back-Cover Texts.
  #############################################################################
 
 
 
 
          自由固不是錢所買到的,但可以爲錢而賣掉。        --- 魯迅
 
      現實中, 在人和計算機交互中, 涉及到最多的就是字符串了.
      以致於大部分的數據輸入都被當作字符串來處理.
      若是說列表是天地, 那字符串就必定是這天地間的橫流.
 
 
  .  newLISP中的字符串
      Strings in newLISP code
 
      newLISP 處理字符串的能力無疑是強大的, 各類方便的刀具都給你備齊了, 每一把都
  是居家宅男, 殺碼越貨, 的必備神器.
 
      廣告完畢, 言歸正傳.~_~~
 
      在nl裏有三種方法能夠表示字符串:
 
      用雙引號圍起來 ;優勢按鍵更少, 並且轉義字符有效, 好比"\n"
      (set 's "this is a string")
 
      用花括號圍起來 ;優勢過濾一切轉義字符
      (set 's {this is a string})
 
      用專門的標識碼圍起來 ;除了上面的優勢外,他還能夠構造大於2048字節的字符串
      (set 's [text]this is a string[/text])
 
 
      第一和第二中方法構建的字符串不能超過 2048 個字節.
      不少人會以爲既然有了第二種, 爲何還要有第一種?
      讓咱們測試下下面的代碼
 
  > {\{}
 
  ERR: string token too long : "\\{}"
 
  > "\""
  "\""
 
      看到沒, 花括號的好處就是過濾一切的轉義字符, 轉義字符到了裏面沒有任何做用.
  若是你要print 一個字符串:
 
 
  > (print {\n road to freedom})
  \n road to freedom"\\n road to freedom"
  > (print "\n road to freedom")
 
   road to freedom"\n road to freedom"
 
      花括號內內的轉義字符沒效了, 根本沒換行. 這三種方法就第一種方法, 能夠在內部
  使用本身的TAG 雙引號.
 
      第二種方法, 花括號, 這種方法我是很是鼓勵使用的, 爲何, 方便啊, 不用在轉義
  字符前加個反斜槓了, 在構造正則表達式的時候尤爲好用.
 
  > (println "\t45")
          45
  "\t45"
  > (println "\\t45")
  \t45
  "\\t45"
  > (println {\t45})
  \t45
  "\\t45"
 
  > (regex "\\d" "a9b6c4")
  ("9" 1 1)
 
  > (regex {\d} "a9b6c4")
  ("9" 1 1)
 
      字符串一般支持如下幾種轉義字符:
 
  character   description
  \"          for a double quote inside a quoted string
  \n          for a line-feed character (ASCII 10)
  \r          for a return character (ASCII 13)
  \t          for a TAB character (ASCII 9)
  \nnn        for a three-digit ASCII number (nnn format between 000 and 255)
  \xnn        for a two-digit-hex ASCII number (xnn format between x00 and xff)
 
  (set 's "this is a string \n with two lines")
  (println s)
 
  this is a string
  with two lines
 
  (println "\110\101\119\076\073\083\080") ; 十進制 ASCII
  newLISP
 
  (println "\x6e\x65\x77\x4c\x49\x53\x50") ; 十六進制 ASCII
  newLISP
 
 
      若是要你反過來把字符串寫成上面的各類數字字符串, 該怎麼呢?
      提示:formatunpack .
 
 
      第三種[text] [\text] 一般用來處理超長的字符串數據(大於 2048 字節), 好比web
  頁面. nL 在傳遞長字符串的時候, 也會自動使用這種格式.
 
 
  (set 'novel (read-file {my-latest-novel.txt}))
  ;->
  [text]
  It was a dark and "stormy" night...
  ...
  The End.
  [/text]
 
      使用 length 能夠獲得字符串的長度:
 
  (length novel)
  ;-> 575196
 
      newLISP 能夠高效的處理數百萬的字符串.
      若是要統計unicode 字符串的長度, 必須使用utf8 版本的 newLISP:
 
  (utf8len (char 955))
  ;-> 1
  (length (char 955))
  ;-> 2
  > (utf8len "個")
  4
  > (length "個")
  2
 
      cmd.exe 在處理非ascii 字符的時候會產生不少問題, 幾乎沒法解決, 可是非Win32
  console 沒這個問題.
 
 
  . 構造字符串
      Making strings
 
 
      有N種方法構造字符串. 處處都是字符串. 遍地都是字符串...
      若是想一個一個字符的構造的話能夠用 char :
 
  (char 33)
  ;-> "!"
 
  > (char "a")
  97
 
  > (char 0x61)
  "a"
 
  > (char 97)
  "a"
 
 
      char 只能處理一個字符, 他能夠將字符轉換成數字, 也能夠將數字轉換成字符.
 
 
  (join (map char (sequence (char "a") (char "z"))))
  ;-> "abcdefghijklmnopqrstuvwxyz"
 
 
      char 得到 "a""z" ascii碼, 而後用sequence 產生一個數字序列, 接着用map
  映射 char 函數到每一個數字, 產生數字相對應的字符. 最後join 將整個列表合成一個字
  符串.
 
      咱們也能夠給 join 傳遞一個參數, 作分隔符.
 
 
  (join (map char (sequence (char "a") (char "z"))) "-")
  ;-> "a-b-c-d-e-f-g-h-i-j-k-l-m-n-o-p-q-r-s-t-u-v-w-x-y-z"
 
 
      和 join 相似 append 也能夠鏈接字符串. (大部分的列表函數也可用於字符串)
 
 
  (append "con" "cat" "e" "nation")
  ;-> "concatenation"
 
 
      構造列表的時候咱們用list , 構造字符串咱們用string .
      string 能夠將各類參數組合成, 一個字符串.
 
 
  (define x 42)
  (string {the value of } 'x { is } x)
  ;-> "the value of x is 42"
 
 
      更精細的字符串輸出可使用format , 稍後就會見到.
      dup 能夠複製字符串:
 
 
  > (dup "帥鍋" 5)
  "帥鍋帥鍋帥鍋帥鍋帥鍋"
 
 
      date 會產生一個包含當前時間信息的字符串.
 
 
  > (date)
  "Mon May 14 15:50:34 2012"
 
 
  > (date 1234567890)
  "Sat Feb 14 07:31:30 2009"
 
 
 
  .  字符串手術
      String surgery
 
 
      這裏不知道怎麼翻譯鳥, 手術啊. 聽起來很恐怖. 其實就是永久性改變.
 
-     不少函數均可以操做字符串, 部分是具備破壞性的(destructive 這些函數在手冊
  , 都有一個 ! 標誌).
 
 
  (set 't "a hypothetical one-dimensional subatomic particle")
  (reverse t)
  ;-> "elcitrap cimotabus lanoisnemid-eno lacitehtopyh a"
  t
  ;-> "elcitrap cimotabus lanoisnemid-eno lacitehtopyh a"
 
 
      以前已經說過要用這些函數又不想破壞原來的數據, 就要用 copy.
 
 
  (reverse (copy t))
  ;-> "elcitrap cimotabus lanoisnemid-eno lacitehtopyh a"
  t
  ;-> "a hypothetical one-dimensional subatomic particle"
 
 
 
      上面的reverse 永久性的改變了 t. 可是下面的大小寫轉換函數, 卻不會改變原字符
  .
 
 
  (set 't "a hypothetical one-dimensional subatomic particle")
  (upper-case t)
  ;-> "A HYPOTHETICAL ONE-DIMENSIONAL SUBATOMIC PARTICLE"
  (lower-case t)
  ;-> "a hypothetical one-dimensional subatomic particle"
  (title-case t)
  ;-> "A hypothetical one-dimensional subatomic particle"
  t
  ;-> "a hypothetical one-dimensional subatomic particle"
 
 
 
  . 子串
      Substrings
 
      若是須要抽取字符串中的一部分能夠用如下的方法:
 
 
  (set 't "a hypothetical one-dimensional subatomic particle")
  (first t)
  ;-> "a"
  (rest t)
  ;-> " hypothetical one-dimensional subatomic particle"
  (last t)
  ;-> "e"
  (t 2)
  ;-> "h"
 
 
      你會發現這和上一章介紹的列表操做好像.nL裏頭大部分的列表操做函數, 也一樣
  能夠操做字符串. 其中就包括各類選取函數.
 
 
  1:  字符串分片
      String slices
 
 
      slice 能夠將從一個現存的字符串中, 分割出一個新的字符串.
 
 
  (set 't "a hypothetical one-dimensional subatomic particle")
  (slice t 15 13) ;從第15個位置開始, 提取出出13個字符
  ;-> "one-dimension"
  (slice t -8 8)  ;從倒數第8個位置開始, 提取出8個字符
  ;-> "particle"
  (slice t 2 -9)  ;從第2個位置開始, 提取到倒數第9個字符爲止(第9個字符不算)
  ;-> "hypothetical one-dimensional subatomic"
  (slice "schwarzwalderkirschtorte" 19 -1) ;同上, 最後一個字符不取
  ;-> "tort"
 
 
      固然, 字符串也能夠用隱式操做.
 
 
  (15 13 t)
  ;-> "one-dimension"
  (0 14 t)
  ;-> "a hypothetical"
 
 
      上面提取的字符串都是連續的. 若是要抽取出分散的字符. 就得用 select :
 
 
  (set 't "a hypothetical one-dimensional subatomic particle")
  (select t 3 5 24 48 21 10 44 8)
  ;-> "yosemite"
  (select t (sequence 1 49 12)) ; 從第1個字符開始, 每隔12個提取出一個字符
  ;-> " lime"
 
  > (help select)
  syntax: (select <string> <list-selection>)
  syntax: (select <string> [<int-index_i> ... ])
 
 
       <list-selection> 列表中包含了要提取的字符的位置.
 
 
 
  2:  改變字符串的首位
      Changing the ends of strings
 
 
      choptrim 能夠給字符串作收尾切除術, 他們都具破壞性.
      切切切...
 
      chop 只能切除一個指定位置的字符...
 
 
  (chop t) ; 默認是最後一個字符
  ;-> "a hypothetical one-dimensional subatomic particl"
  (chop t 9) ; 切除第9個字符
  ;-> "a hypothetical one-dimensional subatomic"
 
 
      trim 修剪掉存在於字符串頭尾的指定字符.
 
  (set 's " centred ")
  (trim s) ; defaults to removing spaces
  ;-> "centred"
 
  (set 's "------centred------")
  (trim s "-")
  ;-> "centred"
 
  (set 's "------centred********")
  (trim s "-" "*") ;能夠分別指定須要修剪的頭 和 尾 "字符"
  ;-> "centred"
 
 
  3:  pushpop 字符串
      push and pop work on strings too
 
 
      push 能夠將元素壓入指定字符串的指定位置. pop  相反.
      若是沒有指定位置, 默認爲字符串的第一個位置.
 
 
  (set 't "some ")
  (push "this is " t)
  (push "text " t -1)
  ;-> t is now "this is some text"
 
 
      pushpop 都返回壓入或者彈出的元素, 而不是目標字符串. 這樣操做大的字符串
  , 就會更快. 不然你就得用slice 屏蔽輸出了.
 
  >(help pop)
  syntax: (pop <str> [<int-index> [<int-length>]])
 
      能夠指定pop字符的數量, [<int-length>] .
 
 
  (set 'version-string (string (sys-info -2)))
  ; eg: version-string is "10402"
  (set 'dev-version (pop version-string -2 2)) ; 老是兩個數字
  ; version-string is now "02"
  (set 'point-version (pop version-string -1)) ; 老是一個數字
  ; version-string is now "4"
  (set 'version version-string) ; 一位或者兩位 99?
  (println version "." point-version "." dev-version " on " ostype)
  10.4.02 on Win32
  "Win32"
 
 
      ostype 返回操做系統類型.
 
 
  . 修改字符串
      Modifying strings
 
      有兩種方法修改字符串, 一種, 指定具體的位置. 第二種指定特定的內容.
 
  1:  經過索引修改字符串
      Using index numbers in strings
 
 
      很久之前是有nth-setset-nth, 不過鑑於各類 set 和 被 set , 其操做方法
  和返回值的複雜性. 在現今的版本中, 他們都已經消失不見了. 不過咱們可使用隱式索
  , 操做訪問指定位置的元素.
 
  > (set 'str "thinking newLISP !")
  "thinking newLISP !"
  > (setf (str 0) "I t")
  "I T"
  > str
  "I Thinking newLISP !"
 
 
  2:  改變字符串的子串
      Changing substrings
 
 
      不少時候, 你沒法確切的知道, 須要操做的字符的索引, 或者找出來的代價太大.\
      這時候就能夠用replace 替換全部符合本身要求的字符串部分...
 
 
  > (help replace)
  syntax: (replace <str-key> <str-data> <exp-replacement>)
  syntax: (replace <str-pattern> <str-data> <exp-replacement> <int-regex-option>)
 
 
  (replace old-string source-string replacement)
  So:
  (set't "a hypothetical one-dimensional subatomic particle")
  (replace "hypoth" t "theor") ;將字符串中全部的hypoth替換成theor
  ;-> "a theoretical one-dimensional subatomic particle"
 
 
  replace 是破壞性函數, 若是你不想改變原來的字符串, 可使用copy 或者 string :
 
 
  (set't "a hypothetical one-dimensional subatomic particle")
  (replace "hypoth" (string t) "theor")
  ;-> "a theoretical one-dimensional subatomic particle"
  t
  ;-> "a hypothetical one-dimensional subatomic particle"
 
 
  3:  使用正則表達式替換字符串內容
      Regular expressions
 
 
      若是你翻閱過手冊, 會發現不少語法裏都會加上一個可選參數, <int-regex-option>
  . 這個參數就是正則表達式數字選項. 具體的數字意義, 能夠在手冊中搜索 PCRE name .
  最經常使用的是0 (大小寫不敏感)1 (大小寫敏感).
 
      nL使用的是Perl-compatible Regular Expressions (PCRE), Perl兼容的正則表達
  . 除了replace, directory, find, find-all, parse, search starts-with,
  ends-with, 都接受正則表達式.
 
 
  (set 't "a hypothetical one-dimensional subatomic particle")
  (replace {h.*?l(?# h followed by l but not too greedy)} t {} 0)
  ;-> "a one-dimensional subatomic particle"
 
 
      在構建正則表達式的時候, 你能夠選用雙引號, 或者花括號, 二者的區別以前已經講
  過了. 我的仍是推薦花括號...
 
 
  (set'str "\s")
  (replace str "this is a phrase" "|" 0) ; 並無搜索替換 \s (空白符)
  ;-> thi| i| a phra|e ; 只替換了字符 s
 
  (set'str "\\s")
  (replace str "this is a phrase" "|" 0)
  ;-> this|is|a|phrase ; 成功替換!
 
  (set'str {\s})
  (replace str "this is a phrase" "|" 0)
  ;-> this|is|a|phrase ; better!
 
 
 
  : 系統變量: $0, $1 ...
      System variables: $0, $1 ...
 
 
      凡是使用 regex 的函數, 都會將匹配的結果綁定到系統變量: $0 $1 ... $15 ,
  以直接使用他們, 也可使用$ 函數來引用他們.
      若是你是正則表達式初學者, 建議搜索pcre 教程. 下面的代碼看的迷糊的不用建議.
  還有手冊, 還有code-pattern, 再不濟還有"狗狗" , 通往nL的路不止一條.
      個人觀點一貫是夠用就好, 因此若是看的不太懂, 能夠跳下去. 等你用多了, 天然就
  會了. 業精於勤荒於嬉.
 
- (set 'quotation {"I cannot explain." She spoke in a low, eager voice,
  with a curious lisp in her utterance. "But for God's sake do what I ask you. Go
  back
  and never set foot upon the moor again."})
 
 
- (replace {(.*?),.*?curious\s*(l.*p\W)(.*?)(moor)(.*)}
  quotation
  (println { $1 } $1 { $2 } $2 { $3 } $3 { $4 } $4 { $5 } $5)
  4) ;出於格式的問題 上面的字符串多了\n換行, 因此我用4 設置了 PCRE_DOTALL
     ;這樣 . 也表明了換行符
 
   $1 "I cannot explain." She spoke in a low $2 lisp  $3 in her utterance. "But f
  r God's sake do what I ask you. Go
  back
  and never set foot upon the  $4 moor $5  again."
 
 
 
      上面每個小括號內的匹配值, 都被綁定到了系統變量,$1$5 ,$0 表明符
  合整個正則表達式的字符串部分. 拗口吧, 蛋疼的看代碼去.
 
 
  (set 'str  "http://newlisp.org:80")
  (find "http://(.*):(.*)" str 0)  → 0
 
  $0  → "http://newlisp.org:80"
  $1  → "newlisp.org"
  $2  → "80"
 
 
  1.  替換部分的表達式
      The replacement expression
 
 
  > (help replace)
  syntax: (replace <str-key> <str-data> <exp-replacement>)
  syntax: (replace <str-pattern> <str-data> <exp-replacement> <int-regex-option>)
 
 
      <exp-replacement>就是替換部分, 你找到的任何符合要求的數據, 均可以用這裏的
  表達式值, 替換. 整個表達式沒有限制, 設置是能夠沒意義的操做.
 
 
  (set 't "a hypothetical one-dimensional subatomic particle")
  (replace {t[h]|t[aeiou]} t (println $0) 0)
  th
  ti
  to
  ti
  t
  ;-> "a hypothetical one-dimensional subatomic particle"
 
 
      整個replace 表達式的目的是, 將字符串裏,t開頭, h或者任何元音字母結尾的字
  符打印出來. <exp-replacement> 就是 (println $0) , 他完成了兩個工做, 1. 打印出
  匹配的單詞, 也有人叫這"反作用". 第二個利用表達式的返回值$0 , 替換遠字符串中匹
  配的值, 而這兩個值是同樣的, 因此原字符串內容看起來沒有任何改變.
 
 
  (replace "a|e|c" "This is a sentence" (upper-case $0) 0)
  ;-> "This is A sEntEnCE"
 
 
      下面的代碼使用了更復雜的<exp-replacement>.
 
 
  (set 't "a hypothetical one-dimensional subatomic particle")
  (set 'counter 0)
- (replace "o" t
- (begin
  (inc 'counter)
  (println {replacing "} $0 {" number } counter)
  (string counter)) ; 替換的部分必須是字符串. 這個值是<exp-replacement>的返回值
  0)
  replacing "o" number 1
  replacing "o" number 2
  replacing "o" number 3
  replacing "o" number 4
  "a hyp1thetical 2ne-dimensi3nal subat4mic particle"
 
 
      begin 將多個表達式組裝成一個表達式, 依次執行, 最後一個表達式, 做爲這個表達
  式組的返回值.
      下面讓咱們看一個replace 的實際應用.
      假設有一個文本文件, "zhuzhu.txt"裏面的內容以下:
 
 
  1 a = 15
  2 another_variable = "strings"
  4 x2 = "another string"
  5 c = 25
  3x=9
 
      如今咱們想將他改爲以下形式, 讓他看起來漂亮點.
 
 
  10 a                   = 15
  20 another_variable    = "strings"
  30 x2                  = "another string"
  40 c                   = 25
  50 x                   = 9
 
 
 
      將下面的代碼保持成ft.lsp . 而後執行 newlisp ft.lsp zhuzhu.txt
 
 
  (set 'file (open ((main-args) 2) "read"))
  ;(set 'file (open "ni.txt" "read"))
  (set 'counter 0)
- (while (read-line file)
-     (set 'temp
-         (replace {^(\d*)(\s*)(.*)} ; 改變開始的數字
              (current-line)
              (string (inc 'counter 10) " " $3 )
              0))
- (println
-     (replace {(\S*)(\s*)(=)(\s*)(.*)} ; 找出有用的數據
          temp
          (string $1 (dup " " (- 20 (length $1))) $3 " " $5)
      0)))
 
 
 
      while 循環不斷的將文件的每一行讀入, 而後(current-line) 獲取當前讀入的行.
  第一個replace 組裝開始的數字, {^(\d*)(\s*)(.*)} 將源字符串分離成, 開始的數字,
  接着的空白符, 和最後的內容. 接着用 (string (inc 'counter 10) " " $3 ) 將前兩部
  分剔除, 剩下第三部分和 counter 值組成新的字符串. counter 每處理一行, 就加 10 .
  替換後的字符串賦值給臨時變量temp.
      第二個replace , 將臨時變量分離成4個部分 {(\S*)(\s*)(=)(\s*)(.*)}.
      \S 表明了除 \s 之外的任何字符.
      從中提取出$1 $3 $5 , 組成新的字符串,
      (string $1 (dup " " (- 20 (length $1))) $3 " " $5)
      爲了對齊, 咱們將$1$3 (也就是等號) , 之間的距離規定成20 , 若是$1 短於
  20個字節則dup 出多餘空格來補充.
 
 
      Regular expressions aren't very easy for the newcomer,
      but they're very powerful, particularly
      with newLISP's replace function, so they're worth learning.
 
      正則表達式也許對於初學者來講比較困難, 可是很是強大, 特別是配合上各類
  newLISP函數後, 能夠大大的提升效率. 平時仍是該多練習下.
 
 
 
 
  . 測試和比較字符串
      Testing and comparing strings
 
 
      有各類各樣的測試函數能夠用到字符串上. 這些比較操做符會依序相互比較字符串的
  每個部分.
 
 
  (> {Higgs Boson} {Higgs boson}) ; nil ;B 比 b 小
  (> {Higgs Boson} {Higgs}) ; true
  (< {dollar} {euro}) ; true
  (> {newLISP} {LISP}) ; true
  (= {fred} {Fred}) ; nil ; f 和 F 不同
  (= {fred} {fred}) ; true
 
 
      從第一個字符開始比較, 直到得出結果.
      比較多個字符串也不是問題. 介於newLISP 優秀的參數處理能力, 你不用再直接寫迭
  代了.
 
 
  (< "a" "c" "d" "f" "h")
  ;-> true
 
 
      若是隻提供一個參數呢?
      nL會爲你提供默認值. 若是提供的是數字, 則假設和0 比較, 若是是字符串, 則假設
  "" 空字符串比較...
 
 
  (> 1) ; true - assumes > 0
  (> "fred") ; true - assumes > ""
 
 
      下面的函數能夠很是方便的分析和提取字符串中的指定內容:
      member , regex , find-all , starts-with , ends-with .
 
 
  (starts-with "newLISP" "new")
  ;-> true
  (ends-with "newLISP" "LISP")
  ;-> true
 
 
      他們也可使用正則表達式參數. (一般使用 01)
 
 
  (starts-with {newLISP} {[a-z][aeiou](?\#lc followed by lc vowel)} 0)
  ;-> true
  (ends-with {newLISP} {[aeiou][A-Z](?\# lc vowel followed by UCase)} 0)
  ;-> false
 
 
      0 表明了PCRE 裏的, 大小寫敏感, 1 則是不敏感.
      find , find-all , member ,regex 查找整個字符串.
      find 返回, 第一個符合要求的元素的位置.
 
 
  (set 't "a hypothetical one-dimensional subatomic particle")
  (find "atom" t)
  ;-> 34
  (find "l" t)
  ;-> 13
  (find "L" t)
  ;-> nil ; 大小寫敏感
 
 
      member 判斷一個字符串是不是另外一個字符串的一部分, 若是是, 則返回子串, 以及
  以後的全部字符.
 
 
  (member "rest" "a good restaurant")
  ;-> "restaurant"
 
 
      findmember 均可以使用正則表達式選項.
 
 
- (set 'quotation {"I cannot explain." She spoke in a low,
  eager voice, with a curious lisp in her utterance. "But for
  Gods sake do what I ask you. Go back and never set foot upon
  the moor again."})
 
  (find "lisp" quotation) ; 沒有正則
  ;-> 69 ; 位於第 69 位 , 即 l 的位置
 
  (find {i} quotation 0) ; with regex
  ;-> 15 ; 位於第 15 位
 
  (find {s} quotation 1) ; 大小寫不敏感
  ;-> 20 ; 位於第 20 位
 
- (println "character "
  (find {(l.*?p)} quotation 0) ": " $0) ; 查找一個字符l 後跟着字符p 的子串
  ;-> character 13: lain." She sp
 
 
      再次提醒,console 命令行下, 輸入多行語句的時候, 先輸入一個回城, 而後才能
  把語句全粘貼上去, 或者在多行語句的首尾兩行, 分別單獨的寫上[cmd]和[/cmd].
 
 
      find-all 的工做方式相似 find , 不過他不只僅是返回第一個匹配子串, 而是以列
  表的形式, 返回全部的匹配子串. 他操做字符串的時候默認使用正則表達式. 因此能夠不
  用顯示的標註, 正則選項.
 
 
  > (help find-all)
  syntax: (find-all <str-regex-pattern> <str-text> [<exp> [<int-regex-option>]])
 
- (set 'quotation {"I cannot explain." She spoke in a low,
  eager voice, with a curious lisp in her utterance. "But for
  Gods sake do what I ask you. Go back and never set foot upon
  the moor again."})
 
 
  (find-all "[aeiou]{2,}" quotation $0) ; 兩個或者更多的原音字母組成的子串
  ;-> ("ai" "ea" "oi" "iou" "ou" "oo" "oo" "ai")
 
 
      find-all 返回的是, 符合要求的內容. 若是還想獲得他們的位置和長度, 就要使用
  regex .
      regex 返回符合要求的每一個子串的內容, 開始位置, 以及長度. 第一次看, 會以爲
  稍顯複雜.
 
- (set 'quotation
  {She spoke in a low, eager voice, with a curious lisp in her utterance.})
 
  (println (regex {(.*)(l.*)(l.*p)(.*)} quotation 0))
  ;-->
- ("She spoke in a low, eager voice, with a curious lisp in
  her utterance." 0 70 "She spoke in a " 0 15 "low, eager
  voice, with a curious " 15 33 "lisp" 48 4 " in her
  utterance." 52 18)
 
 
      首先返回的就是符合整個正則表達式要求的字符串. 也是最長的,0 開始 長達
  70 字節. 而後就是第一個第一個括號內匹配的內容, 從位置 0 開始 ,15 個字節.
  第二個括號(分組)內的數據, 從第 15 位開始,33 字節....
 
      這些匹配的分組全被放到系統變量裏.
 
 
- (for (x 1 4)
  (println {$} x ": " ($ x)))
  $1: She spoke in a
  $2: low, eager voice, with a curious
  $3: lisp
  $4: in her utterance.
 
 
  . 字符串轉換成列表
      Strings to lists
 
 
      先讓咱們看看 "聞名遐邇"explode ,  他能夠將字符串按指定的大小炸成一段段
  的子串, 而後以列表的形式返回全部子串.
 
 
  (set 't "a hypothetical one-dimensional subatomic particle")
  (explode t)
 
- :-> ("a" " " "h" "y" "p" "o" "t" "h" "e" "t" "i" "c" "a" "l"
  " " "o" "n" "e" "-" "d" "i" "m" "e" "n" "s" "i" "o" "n" "a"
  "l" " " "s" "u" "b" "a" "t" "o" "m" "i" "c" " " "p" "a" "r"
  "t" "i" "c" "l" "e")
 
  > (help explode)
  syntax: (explode <str> [<int-chunk> [<bool>]])
  syntax: (explode <list> [<int-chunk> [<bool>]])
 
  (explode (replace " " t "") 5)
  ;-> ("ahypo" "theti" "calon" "e-dim" "ensio" "nalsu" "batom" "icpar"
  "ticle")
 
 
      int-chunk 就是分塊的大小, bool 決定是否要拋棄最後不滿int-chunk 長度的子串.
      你有開天斧, 我有補天石.
      joinexplode 作的恰好相反, 將一個全是字符串元素的列表組裝成一個新的字符
  .
 
 
  >(help join)
  syntax: (join list-of-strings [str-joint [bool-trail-joint]])
 
 
  set 'lst '("this" "is" "a" "sentence"))
 
  (join lst " ")  → "this is a sentence"
 
  (join (map string (slice (now) 0 3)) "-")  → "2012-5-16" ;將數字中
 
  (join (explode "keep it together"))  → "keep it together"
 
  (join '("A" "B" "C") "-")         → "A-B-C"
  (join '("A" "B" "C") "-" true)    → "A-B-C-"
 
 
      find-all 也能夠分割字符串.
 
 
  (find-all ".{3}" t) ; 默認使用正則表達式
  characters
  ;-> ("a h" "ypo" "the" "tic" "al " "one" "-di" "men" "sio"
  "nal" " su" "bat" "omi" "c p" "art" "icl")
 
 
  . 分析字符串
      Parsing strings
 
      接下來這個函數絕對會讓你"聲淚俱下".
      若是你須要常常頻繁的處理大範圍的文本數據的時候. parse 絕對是你的至寶.
      他讓你的數據統計分析, 再也不痛苦. (nL內部還有不少專業的統計學函數)
 
 
  > (help parse)
  syntax: (parse <str-data> [<str-break>  [<int-option>]])
 
 
      parse 根據<str-break> 來分割字符串. 字符串中的 <str-break> 會被吃掉. 剩下
  判斷, 做爲一個個子串組成列表返回.
 
 
  (parse t) ; 默認的分隔符爲空格...
  ;-> ("a" "hypothetical" "one-dimensional" "subatomic" "particle")
 
 
      <str-break> 能夠是單個的分割符 , 也能夠是字符串.
 
  (set 'pathname {/System/Library/Fonts/Courier.dfont})
  (parse pathname {/})
  ;-> ("" "System" "Library" "Fonts" "Courier.dfont")
 
 
  (set 't {spamspamspamspamspamspamspamspam})
  ;-> "spamspamspamspamspamspamspamspam"
  (parse t {am}) ; break on "am"
  ;-> ("sp" "sp" "sp" "sp" "sp" "sp" "sp" "sp" "")
 
 
      咱們能夠用filter 將結果列表中的, 空格字符串, 過濾掉.
 
 
  (filter (fn (s) (not (empty? s))) (parse t {/}))
  ;-> ("System" "Library" "Fonts" "Courier.dfont")
 
 
      過濾HTML-tag:
 
 
  (set 'html (read-file "/Users/Sites/index.html"))
  (println (parse html {<.*?>} 4)) ; option 4: dot matches newline
 
 
      nL同時提供了專門的XML分析工具: xml-parse . 後面會有專門一整章介紹.
 
      在咱們沒有明確指定的 <str-break> 的時候, nL 使用內部的分析規則. 這時候的算
  法和指定後的算法也不同.
 
      When no str-break is given, parse tokenizes according to newLISP's
      internal parsing rules.
 
 
- (set 't {Eats, shoots, and leaves ; a book by Lynn Truss})
  (parse t)
  ;-> ("Eats" "," "shoots" "," "and" "leaves") ; she's gone!
 
 
      由於沒有指定界定符, 因此 ";" 以後的內容都被斷定成了註釋.
      若是要讓parse 按你的規則分離數據, 就必須提供明確的界定符或者正則表達式.
 
 
- (set 't {Eats, shoots, and leaves ; a book by Lynn Truss})
  (parse t " ")
  ;-> ("Eats," "shoots," "and" "leaves" ";" "a" "book" "by" "Lynn" "Truss")
 
      或者
 
  (parse t "\\s" 0) ; {\s} 是空白字符
  ;-> ("Eats," "shoots," "and" "leaves" ";" "a" "book" "by" "Lynn" "Truss")
 
 
      另外一種分割字符串的方法就是使用 find-all .
 
  (set 'a "1212374192387562311")
  (println (find-all {\d{3}|\d{2}$|\d$} a))
  ;-> ("121" "237" "419" "238" "756" "231" "1")
 
  ; 二選一
 
  (explode a 3)
  ;-> ("121" "237" "419" "238" "756" "231" "1")
 
 
      parse 會界定符吃掉,find-all 則是留下來.
 
 
  (find-all {\w+} t ) ; 匹配一個英文字母、數字或下劃線;等價於[0-9a-zA-Z_]
  ;-> ("Eats" "shoots" "and" "leaves" "a" "book" "by" "Lynn" "Truss")
 
  (parse t {\w+} 0 ) ; 吃掉界定符
  ;-> ("" ", " ", " " " " ; " " " " " " " " " "")
 
  (parse t {[^\w]+} 0 )
  ;->("Eats" "shoots" "and" "leaves" "a" "book" "by" "Lynn" "Truss")
 
  (append '("") (find-all {[^\w]+} t ) '(""))
  ;-> ("" ", " ", " " " " ; " " " " " " " " " "")
 
 
 
  . 其餘的字符串函數
      Other string functions
 
 
      search 在文件中搜索符合要求的字符串. 並返回第一個符合要求的字符串的位置,
  而後將文件指針移到字符串頭的位置(默認狀況下),<bool-flag>true 值時 ,
  移字符串末尾. 下次search 的時候, 從當前文件指針的位置繼續開始.
 
  > (help search )
  syntax: (search <int-file> <str-search> [<bool-flag> [<int-options>]])
 
 
  (set 'f (open {/private/var/log/system.log} {read}))
  (search f {kernel})
  (seek f (- (seek f) 64)) ; rewind file pointer
- (dotimes (n 3)
  (println (read-line f)))
  (close f)
 
 
      上面的代碼從系統日誌中搜索包含 kernel 的字符串, 而後從找到的位置回溯 64
  個字節, 讀取一行日誌, 並打印出來.
      更多的字符串相關函數, 能夠在手冊中搜索 String and conversion functions .
 
 
 
  十一. 格式化字符串
        String and conversion functions
 
 
      和其餘的語言同樣, nL也提供了優雅的字符串輸出更能 (format 函數).
      假設咱們須要打印以下的內容:
 
  folder: Library
  file: mach
 
      咱們須要使用以下的字符串模板:
 
  "folder: %s" ; or
  " file: %s"
 
      提供給 format 一個文字模板, 以後依序接上全部模板中須要的參數.
 
 
  (format "folder: %s" f) ; or
  (format " file: %s" f)
 
  > (help format)
  syntax: (format <str-format> [<exp-data-1> <exp-data-2> ... ])
  syntax: (format <str-format> <list-data>)
 
 
      <str-format> 就是字符串模板, 只有一個. 其後的參數都是編碼中相對應的數據.
  (format " file: %s" f) 爲例, 這裏提供的 f 是字符串, 前面模板裏就必須放一個
  %s , 若是提供的 f 是數字, 前面的模板就必須放一個 %d . 目前支持 11 種數據類型.
 
 
  format  description
  s       text string
  c       character (value 1 - 255)
  d       decimal (32-bit)
  u       unsigned decimal (32-bit)
  x       hexadecimal lowercase
  X       hexadecimal uppercase
  o       octal (32-bits) (not supported on all compilers)
  f       floating point
  e       scientific floating point
  E       scientific floating point
  g       general floating point
 
 
      相似必須匹配, 不然會報錯. %至關於轉義字符, 他的位置表明了後面的數據在字符
  串中的位置.
 
 
  (set 'f "OneLisp")
  (format "folder: %s" f)
  ;-->"folder: OneLisp"
 
  (format "%s folder: " f)
  "OneLisp folder: "
 
  (format "%d" "abc")
  ;-->ERR: data type and format don't match in function format : "abc"
 
 
      下面的代碼使用 directory 函數打印出當前目錄下全部的文件和目錄.
 
 
- (dolist (f (directory))
-     (if (directory? f)
          (println (format "folder: %s" f))
          (println (format " file: %s" f))))
 
  ;輸出
 
  folder: .
  folder: ..
  folder: api
   file: cd.dll
   file: cmd-lisp.bat
  folder: code
   file: CodePatterns-cn.html
   file: CodePatterns-CN.html.bak
   file: CodePatterns.html
   file: COPYING
   file: demo-stdin.lsp
   file: drag.bat
  folder: examples
   file: freetype6.dll
   file: gs.bat
  folder: guiserver
   file: guiserver-keyword.txt
  ...
 
 
      format 裏的字符串模板還能夠就行更精細的輸出控制.
 
  "%w.pf"
 
      f 就是以前介紹的數據類型標誌, 必選.
      w 是這個數據輸出時, 佔用的寬度.
      p 是這個數據輸出時, 的精度.
      w以前能夠跟, 負號(右對齊), 正號(左對齊), 0 (空位用0填滿) ,  默認是右對齊.
      填 0 只在右對齊的時候有用.
 
 
  >(format "Result = %05d" 2)
   "Result = 00002"
 
  > (format "Result = %+05d" 2)
  "Result = +0002"
  > (format "Result = %+05d" -2)
  "Result = -0002"
  > (format "Result = %-05d" -2)
  "Result = -2   "
  > (format "Result = %05d" -2)
  "Result = -0002"
 
 
      下面來個複雜點的例子. 打印位於 32 - 400 內的全部字符, 並輸出他們的十進制,
  十六進制, 和二進制內容.
      由於format 沒法輸出二進制數據, 因此專門寫了個二進制轉換函數. 如今有個現成
  bits 能夠轉換 2 進制了.
 
 
- (define (binary x , results)
-   (until (<= x 0)
      (push (string (% x 2)) results) ;使用 % 求餘, 表明每一位的二進制數
      (set 'x (/ x 2))) ; 從新設置 x
    results)
 
 
- (for (x 32 0x01a0)
-   (println (char x) ; 先用char將數字轉換成字符
-     (format "%4d\t%4x\t%10s" ; 十進制 \t 十六進制 \t 二進制字符串
              (list x x (join (binary x))))))
 
 
  x 120     78       1111000
  y 121     79       1111001
  z 122     7a       1111010
  { 123     7b       1111011
  | 124     7c       1111100
  } 125     7d       1111101
  ~ 126     7e       1111110
 
 
  十二.newLISP思考
        Strings that make newLISP think
 
 
      爲何用這個標題, 嘿嘿, 最後有個很好玩的例子. 你甚至能夠寫個, 代碼混亂生成
  , 看看你會獲得些什麼.
 
      本章最後介紹的兩個函數: eval , eval-string .
      這兩個函數專門負責執行nL代碼.
      只要你提供的代碼能經過檢測, 他們就會返回給你結果.
 
      eval 接受表達式:
 
  (set 'expr (+ 1 2))
  (eval expr)
  ;-> 3
 
 
      eval-string 只接受字符串:
 
  (set 'expr "(+ 1 2)")
  (eval-string expr)
  ;-> 3
 
 
      使用這兩個函數你能夠執行任何的nL代碼. 在咱們默認執行的各類表達式中, 都隱含
  了他們的身影. 他們被默認的執行着, 而你必定不能忘記他們曾經來過, 不然你極可能成
  爲一團漿糊. 當你對 symbol , 對 宏 對各類表達式的本質和他們的計算迷惑的時候,
  來從新看看這句話, 你會豁然開朗.
      eval 爲何重要, 由於他表明了自主選擇, 你能夠在任何須要的時間 , 須要的地點
  執行須要的代碼. 特別是在操做宏的時候, 你的感覺會更深.
 
      下面是段很是有趣的代碼, 他能夠不斷的重組列表, 而後調用 eval-string 執行他
  , 直到 某個表達式獲得執行後, 才結束.
 
 
  (set 'code '(")" "set" "'valid" "true" "("))
  (set 'valid nil)
- (until valid
      (set 'code (randomize code)) ; 使用radomize 打亂 code 序列
      (println (join code " "))
      (eval-string (join code " ") MAIN nil))
 
  ;輸出
 
  ) true 'valid ( set
  'valid ) ( set true
  true set ( 'valid )
  'valid true ( set )
  'valid ( true set )
  ) true ( set 'valid
  ) ( set 'valid true
  'valid ) set true (
  ...
  true set ) ( 'valid
  true ( 'valid ) set
  true 'valid ( set )
  true ) 'valid ( set
  ( set 'valid true )
  true
 
 
 
 
 
  到目前爲止newLISP的基礎, 基本上算是介紹的差很少了, 接下來介紹的會比較深刻點.
  context 和 宏 .
  不過在nL裏這些不管是看起來仍是用起來, 仍是原理上都很是簡潔明瞭.
  Good Luck !!!
 
  彩色版本到http://code.google.com/p/newlisp-you-can-do下載使用scite4newlisp觀看
 
  2012-05-14 - 2012-05-17 15:10:29
相關文章
相關標籤/搜索