任何具有一流功能的语言都可以做到这一点。实际上,您对“高阶”的使用是有说服力的;必要的抽象确实是一个高阶函数。这个想法是编写一个函数applyIf
,它接受一个布尔值(启用/禁用)、一个控制流运算符(实际上只是一个函数)和一个代码块(函数域中的任何值);然后,如果布尔值为真,则将运算符/函数应用于块/值,否则块/值只是运行/返回。这在代码中会更加清晰。
例如,在 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) x
与f x
if相同cond == True
,并且与 else 相同id x
;当然,id x
是一样的x
。
然后你可以把这个模式分解到我们的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
要点是一样的——我们将可能做这件事的逻辑包装在它自己的函数中,然后将其应用于相关的代码块。
请注意,此函数实际上比仅处理代码块更通用(正如 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
以及applyFilter
, someFilter
, body
, runOnThread
, numberOfThreads
, 和的一些虚假定义getConfig
:
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