일급 함수를 사용하는 모든 언어가이 문제를 해결할 수 있습니다. 사실, "고차원"을 사용하는 것은 말하고 있습니다. 필요한 추상화는 참으로 고차 함수가 될 것입니다. 이 아이디어는 부울 (활성화/비활성화), 제어 흐름 연산자 (실제로는 함수) 및 코드 블록 (함수 도메인의 모든 값)을 사용하는 함수 applyIf
을 작성하는 것입니다. 부울이 참이면 연산자/함수가 블록/값에 적용되고 그렇지 않으면 블록/값이 단지 실행/반환됩니다. 이것은 코드에서 훨씬 더 명확해질 것입니다. 하스켈에서
는, 예를 들어,이 패턴으로 작성, 명시 적
applyIf
없는 것 :
id
그냥 신원 기능
\x -> x
입니다 여기에
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
; 항상 그 주장을 되 돌린다. 따라서 (if cond then f else id) x
은 cond == True
인 경우 f x
과 같으며, 그렇지 않은 경우 id x
과 동일합니다. 물론 id x
은 x
과 동일합니다.
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
의 일부 특정 사용이 응용 프로그램의 일반적인 패턴 인 경우, 당신은 할 수 추상적 위에 :
그럼 당신은 우리의 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
위에서 언급했듯이 하스켈은 분명히이 아이디어를 표현할 수있는 유일한 사람이 아닙니다. 예를 들어 여기 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-이-것은 논리를 마무리하고 관련 블록에 그 적용입니다 암호.
이 기능은 코드 블록을 작업하는 것보다 실제로 더 일반적이라는 점에 유의하십시오 (하스켈 형식 서명이 표현함). 예를 들어 abs n = applyIf (n < 0) negate n
을 작성하여 절대 값 기능을 구현할 수도 있습니다. 핵심은 코드 블록 자체가을 통해 추상화 될 수 있다는 것을 인식하는 것입니다. 따라서 if 문과 for 루프와 같은 것들이 함수 일 수 있습니다. 그리고 우리는 이미 함수를 작성하는 방법을 알고 있습니다!
또한 위의 코드는 모두 컴파일 및 실행되지만 일부 가져 오기 및 정의가 필요합니다. 루비 예를 들어
applyFilter = False
someFilter = False
body = putStrLn "Hello, world!"
runOnThread = True
getConfig = return 4 :: IO Int
numberOfThreads = id
, 당신 : 하스켈 예를 들어, applyFilter
, someFilter
, body
, runOnThread
, numberOfThreads
및 getConfig
의 일부 가짜 정의와 함께 impots에게
import Control.Applicative -- for (<$>)
import Control.Monad -- for when, void, and forM_
import Control.Concurrent -- for forkIO and threadDelay
이 필요합니다 수입이없고 다음과 같은 유사한 가짜 정의가 필요합니다.
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
그게'if (! apply_filter || 필터())'? – gilly3