2013-02-16 47 views
1

有時,我有一個控制結構(如果,爲...),並根據條件我要麼使用控制結構,要麼只執行正文。作爲一個簡單的例子,我可以做在C以下,但它是很醜陋:有更高階條件的語言嗎?

#ifdef APPLY_FILTER 
if (filter()) { 
#endif 
    // do something 
#ifdef APPLY_FILTER 
} 
#endif 

另外,如果我只能在運行apply_filter知道這是行不通的。當然,在這種情況下,我可以將代碼更改爲:

if (apply_filter && filter()) 

但這在任意控制結構的一般情況下不起作用。 (我手邊沒有一個很好的例子,但最近我有一些代碼會從這樣的功能中受益匪淺)。

有沒有什麼可以應用條件來控制結構的語言,也就是說有更高階的條件?在僞代碼中,上面的例子將是:

<if apply_filter> 
if (filter()) { 
    // ... 
} 

或者更復雜的例子中,如果varable設置渦卷代碼中的函數,並且啓動它作爲一個線程:

<if (run_on_thread)> 
    void thread() { 
<endif> 

    for (int i = 0; i < 10; i++) { 
     printf("%d\n", i); 
     sleep(1); 
    } 

<if (run_on_thread)> 
    } 
    start_thread(&thread); 
<endif> 

(實際上,在這個例子中,我可以想象,給元條件命名甚至是有用的,以確保頂部和底部s同步。)

我可以想象這樣的事情是LISP中的一個特性,對吧?

+0

不應該是'if(!apply_filter ||過濾器())'? – gilly3 2013-02-20 21:37:20

回答

2

Common Lisp不允許你重新定義if。但是,您可以在Lisp中將自己的控制結構創建爲一個宏,然後使用它。

6

任何具有一流功能的語言都可以解決這個問題。事實上,你對「高階」的使用是明顯的;必要的抽象確實是一個更高階的函數。這個想法是寫一個函數applyIf,該函數接受一個布爾(啓用/禁用),一個控制流操作符(實際上只是一個函數)和一個代碼塊(函數域中的任何值)。那麼,如果布爾值爲true,則將運算符/函數應用於塊/值,否則塊/值將運行/返回。這將在代碼中更清晰。

在Haskell,例如,這種格局將是,沒有一個明確的applyIf,寫成:

example1 = (if applyFilter then when someFilter else id) body 
example2 = (if runOnThread then (void . forkIO) else id) . forM_ [1..10] $ \i -> 
      print i >> threadDelay 1000000 -- threadDelay takes microseconds 

這裏,id僅僅是身份的功能\x -> x;它總是返回它的參數。因此,(if cond then f else id) xf x相同,如果是cond == True,則與id x相同;否則;當然,id xx相同。

然後,你可以考慮此因素模式伸到我們applyIf組合子:

applyIf :: Bool -> (a -> a) -> a -> a 
applyIf True f x = f x 
applyIf False _ x = x 
-- Or, how I'd probably actually write it: 
--  applyIf True = id 
--  applyIf False = flip const 
-- Note that `flip f a b = f b a` and `const a _ = a`, so 
-- `flip const = \_ a -> a` returns its second argument. 

example1' = applyIf applyFilter (when someFilter) body 
example2' = applyIf runOnThread (void . forkIO) . forM_ [1..10] $ \i -> 
       print i >> threadDelay 1000000 

然後,當然,如果applyIf一些特定的用途是在您的應用程序的通用模式,你可以在它摘要:

-- Runs its argument on a separate thread if the application is configured to 
-- run on more than one thread. 
possiblyThreaded action = do 
    multithreaded <- (> 1) . numberOfThreads <$> getConfig 
    applyIf multithreaded (void . forkIO) action 

example2'' = possiblyThreaded . forM_ [1..10] $ \i -> 
       print i >> threadDelay 1000000 

如上所述,Haskell當然不是唯一能夠表達這種想法的人。例如,這是Ruby的翻譯,但要注意我的Ruby非常生鏽,所以這可能是單一的。 (我歡迎對如何改進的建議。)

def apply_if(use_function, f, &block) 
    use_function ? f.call(&block) : yield 
end 

def example1a 
    do_when = lambda { |&block| if some_filter then block.call() end } 
    apply_if(apply_filter, do_when) { puts "Hello, world!" } 
end 

def example2a 
    apply_if(run_on_thread, Thread.method(:new)) do 
    (1..10).each { |i| puts i; sleep 1 } 
    end 
end 

def possibly_threaded(&block) 
    apply_if(app_config.number_of_threads > 1, Thread.method(:new), &block) 
end 

def example2b 
    possibly_threaded do 
    (1..10).each { |i| puts i; sleep 1 } 
    end 
end 

的一點是相同的,我們結束了,也許-DO-這-事情的邏輯在其自身的功能,然後應用到的相關版塊碼。請注意,這個函數實際上比僅僅處理代碼塊更加通用(正如Haskell類型簽名所表示的那樣)。例如,您也可以編寫abs n = applyIf (n < 0) negate n來實現絕對值函數。關鍵是要認識到自己的可以被抽象出來,所以像if語句和for循環可以只是函數。而且我們已經知道如何編寫功能!

此外,上述所有代碼都會編譯和/或運行,但您需要一些導入和定義。對於Haskell的例子,你需要的impots

import Control.Applicative -- for (<$>) 
import Control.Monad  -- for when, void, and forM_ 
import Control.Concurrent -- for forkIO and threadDelay 

applyFiltersomeFilterbodyrunOnThreadnumberOfThreadsgetConfig一些虛假的定義一起:

applyFilter  = False 
someFilter  = False 
body   = putStrLn "Hello, world!" 
runOnThread  = True 
getConfig  = return 4 :: IO Int 
numberOfThreads = id 

爲Ruby的例子,你將不需要進口和以下類似僞造定義:

def apply_filter; false; end 
def some_filter; false; end 
def run_on_thread; true; end 
class AppConfig 
    attr_accessor :number_of_threads 
    def initialize(n) 
    @number_of_threads = n 
    end 
end 
def app_config; AppConfig.new(4); end