2013-02-16 3 views
1

Иногда у меня есть структура управления (if, for, ...), и в зависимости от условия я либо хочу использовать структуру управления, либо только выполнить тело. В качестве простого примера, я могу сделать следующее в 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> 

(На самом деле, в этом примере я мог бы представить, что было бы даже полезно указать мета условие, чтобы убедиться, что верхняя и нижняя части находятся в синхронизации.)

Я мог представить, что что-то вроде этой функции в LISP, не так ли?

+0

SH ouldn't это будет 'if (! apply_filter || фильтр()) '? – gilly3

ответ

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) 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 
Смежные вопросы