Любой язык с первоклассными функциями может снять это. На самом деле, вы говорите о своем использовании «высшего порядка»; необходимая абстракция действительно будет функцией более высокого порядка. Идея состоит в том, чтобы написать функцию 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) x
совпадает с f x
, если cond == True
, и это то же самое, что и у 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 и для циклов, могут быть просто функциями. И мы уже знаем, как создавать функции!
Кроме того, весь приведенный выше код компилируется и/или запускается, но вам понадобятся некоторые импорты и определения. Для примеров 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
SH ouldn't это будет 'if (! apply_filter || фильтр()) '? – gilly3