是否存在具有更高阶条件的语言?

时间:2013-02-16 11:16:36

标签: language-design control-structure

有时,我有一个控制结构(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>

(实际上,在这个例子中,我可以想象给元条件赋一个名称甚至是有用的,以确保顶部和底部s同步。)

我可以想象这样的东西是LISP中的一个功能,对吗?

2 个答案:

答案 0 :(得分: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 xcond == 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

重点是 - 我们在自己的函数中包含了可能做的事物逻辑,然后将其应用于相关的代码块。

请注意,此函数实际上比仅处理代码块更通用(因为Haskell类型签名表示);例如,您也可以编写abs n = applyIf (n < 0) negate n来实现绝对值函数。关键是要意识到代码块本身可以被抽象出来,所以像if语句和for循环这样的东西可以只是函数。我们已经知道如何撰写函数了!

此外,上面的所有代码都编译和/或运行,但您需要一些导入和定义。对于Haskell示例,您将需要impot

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

答案 1 :(得分:2)

Common Lisp不允许您重新定义if。但是,您可以在Lisp中将自己的控制结构发明为宏,并使用它。