## 問題 Problem
有人需要在限制重重的地方運行部分你的代碼,或者你的語言無法簡潔地表達其他領域的問題。
Someone else needs to run parts of your code in a controlled fashion. Alternately, your language of choice cannot express the problem domain in a concise fashion.
## 方案 Solution
使用解釋器模式創建一個領域語言,翻譯成特殊的代碼。
Use the Interpreter pattern to create a domain-specific language that you translate into specific code.
例如,有用戶想在你的程序中執行數學計算。你可以讓他們把代碼交給_eval_運行,但是他們有可能會運行各種各樣的代碼。更好的方案,你可以提供一個小型的“棧計算器”語言,單獨解析,這樣可以只允許運行數學操作,同時還可以反饋更有用的錯誤信息。
Assume, for example, that the user wants to perform math inside of your application. You could let them forward code to?_eval_?but that would let them run arbitrary code. Instead, you can provide a miniature “stack calculator” language that you parse separately in order to only run mathematical operations while reporting more useful error messages.
~~~
class StackCalculator
parseString: (string) ->
@stack = [ ]
for token in string.split /\s+/
@parseToken token
if @stack.length > 1
throw "Not enough operators: numbers left over"
else
@stack[0]
parseToken: (token, lastNumber) ->
if isNaN parseFloat(token) # Assume that anything other than a number is an operator
@parseOperator token
else
@stack.push parseFloat(token)
parseOperator: (operator) ->
if @stack.length < 2
throw "Can't operate on a stack without at least 2 items"
right = @stack.pop()
left = @stack.pop()
result = switch operator
when "+" then left + right
when "-" then left - right
when "*" then left * right
when "/"
if right is 0
throw "Can't divide by 0"
else
left / right
else
throw "Unrecognized operator: #{operator}"
@stack.push result
calc = new StackCalculator
calc.parseString "5 5 +" # => { result: 10 }
calc.parseString "4.0 5.5 +" # => { result: 9.5 }
calc.parseString "5 5 + 5 5 + *" # => { result: 100 }
try
calc.parseString "5 0 /"
catch error
error # => "Can't divide by 0"
try
calc.parseString "5 -"
catch error
error # => "Can't operate on a stack without at least 2 items"
try
calc.parseString "5 5 5 -"
catch error
error # => "Not enough operators: numbers left over"
try
calc.parseString "5 5 5 foo"
catch error
error # => "Unrecognized operator: foo"
~~~
## 討論 Discussion
如果不自己寫解釋器, 你可以結合目前有的CoffeeScrtipt解釋器,以某種方式,這種方式使用它平常的語法創建算法的更加自然的(因此更易于理解)表達方式。
As an alternative to writing our own interpreter, you can co-op the existing CoffeeScript interpreter in a such a way that its normal syntax makes for more natural (and therefore more comprehensible) expressions of your algorithm.
~~~
class Sandwich
constructor: (@customer, @bread='white', @toppings=[], @toasted=false)->
white = (sw) ->
sw.bread = 'white'
sw
wheat = (sw) ->
sw.bread = 'wheat'
sw
turkey = (sw) ->
sw.toppings.push 'turkey'
sw
ham = (sw) ->
sw.toppings.push 'ham'
sw
swiss = (sw) ->
sw.toppings.push 'swiss'
sw
mayo = (sw) ->
sw.toppings.push 'mayo'
sw
toasted = (sw) ->
sw.toasted = true
sw
sandwich = (customer) ->
new Sandwich customer
to = (customer) ->
customer
send = (sw) ->
toastedState = sw.toasted and 'a toasted' or 'an untoasted'
toppingState = ''
if sw.toppings.length > 0
if sw.toppings.length > 1
toppingState = " with #{sw.toppings[0..sw.toppings.length-2].join ', '} and #{sw.toppings[sw.toppings.length-1]}"
else
toppingState = " with #{sw.toppings[0]}"
"#{sw.customer} requested #{toastedState}, #{sw.bread} bread sandwich#{toppingState}"
send sandwich to 'Charlie' # => "Charlie requested an untoasted, white bread sandwich"
send turkey sandwich to 'Judy' # => "Judy requested an untoasted, white bread sandwich with turkey"
send toasted ham turkey sandwich to 'Rachel' # => "Rachel requested a toasted, white bread sandwich with turkey and ham"
send toasted turkey ham swiss sandwich to 'Matt' # => "Matt requested a toasted, white bread sandwich with swiss, ham and turkey"
~~~
上面這個例子,允許連續調用多個函數,因為每個函數都會返回修改后的對象,這樣外圍的函數就可以輪流地修改該對象。借用一個一個非常小的_to_,該例子給于構造過程了一個更加自然的語法,并且如果使用正確的話,讀起來就像愛那個一個自然的句子。這樣的話,你的CoffeeScript技術和你在使用的語言技術都能幫助你找到代碼的問題。
This example allows for layers of functions by how it returns the modified object so that outer functions can modify it in turn. By borrowing a very and the particle?_to_, the example lends natural grammar to the construction and ends up reading like an actual sentence when used correctly. This way, both your CoffeeScript skills and your existing language skills can help catch code problems.
- 貢獻
- 作者
- 授權協議
- 1、Syntax
- 在服務端和客戶端重用代碼
- For循環
- 嵌入JavaScript代碼
- 值域
- 2、Classes and Objects
- 類方法和實例方法
- CoffeeScript式的Type函數
- 鏈式調用
- 克隆對象(深度克隆)
- 不存在就賦值為對象字面量
- 類變量
- 3、Strings
- 分割字符串
- 字符串匹配
- 查找子字符串
- 讓整個字符串小寫
- 大寫整個字符
- 去掉字符串首尾的空白
- 生成唯一的ID
- 首字母大寫
- 重復字符串
- 字符串插值
- 4、Arrays
- Python式的Zip函數 Python-like Zip Function
- 數組去重 Removing Duplicate Elements from Arrays
- 基于數組構建字典對象 Creating a dictionary Object from an Array
- 數組轉成字符串 Creating a String from an Array
- 檢查每一個元素 Testing Every Element
- 數組最大值
- 過濾數組 Filtering Arrays
- 定義區間數組 Define Ranges Array
- 轉置數組 Reversing Arrays
- 化簡數組 Reducing Arrays
- 使用數組來做值交換 Using Arrays to Swap Variables
- 列表解析 List Comprehensions
- 檢查值的類型是否是數組
- 連接數組
- 攪亂數組中的元素 Shuffling Array Elements
- 數組映射 Mapping Arrays
- 5、Dates and Times
- Calculate Phase of the Moon for a Date
- 找出某月的最后一天是幾號 Finding the Last Day of the Month
- 獲取兩個日期相差的天數 Get Days Between Two Dates
- 計算復活節島日期 Calculate the Date of Easter Sunday
- 計算感恩節的日期(美國和加拿大) Calculate the Date of Thanksgiving (USA and Canada)
- 計算上一個(下一個)月份 Finding Last (or Next) Month
- 6、Math
- 快速逆平方根
- 一個隨機整數的函數
- 更快的斐波那契算法
- 生成可預測的隨機數
- 弧度與度轉換
- 生成隨機數
- 數學常數
- 7、Functions
- 反抖動函數 Debounce Functions
- 參數數組化 Splat Arguments
- 當函數調用的括號不可以省略時 When Function Parentheses Are Not Optional
- 遞歸函數 Recursive Functions
- 8、Metaprogramming
- 擴展內置對象 Extending Built-in Objects
- 檢測并創建缺失的函數 Detecting and Creating Missing Functions
- 9、jQuery
- 回調綁定
- 創建jQuery插件
- AJAX
- 10、Ajax
- 不依賴jQuery的Ajax請求 Ajax Request Without jQuery
- 11、Regular Expressions
- 替換子字符串 Replacing Substrings
- 使用定點 Using Heregexes
- 使用HTML字符實體替換HTML標簽 Replacing HTML Tags with HTML Named Entities
- 搜索子字符串 Searching for Substrings
- 12、Networking
- 簡單的服務器
- 雙向客戶端
- 最簡單的HTTP客戶端
- 最簡單的HTTP服務器
- 簡單的客戶端
- 雙向服務端 Bi-Directional Server
- 13、Design Patterns
- 命令模式
- 單例模式
- 策略模式 Strategy Pattern
- 建造者模式 Builder Pattern
- 備忘錄模式 Memento Pattern
- 解釋器模式 Interpreter Pattern
- 裝飾者模式
- 橋接模式
- 工廠方法模式
- 14、Databases
- MongoDB
- SQLite
- 15、Testing
- 使用Jasmine測試