3

是否可以在 Turtle 库 (Haskell) 中拆分 Shell 并对 shell 的拆分执行不同的操作,以便原始 Shell 仅运行一次?

             /---- shell2
---Shell1 --/
            \
             \-----shell3

比如怎么做

do
  let lstmp = lstree "/tmp"
  view lstmp
  view $ do
    path <- lstmp
    x <- liftIO $ testdir path
    return x

这样 lstree "/tmp" 只会运行一次。

具体来说,我想使用输出将 Shell 2 和 Shell 3 发送到不同的文件。

4

2 回答 2

3

您将无法将 a 拆分Shell为同时运行的两个单独的 shell,除非有一些我不知道的魔法。但是文件写入是对 shell 内容或其他一系列事物的折叠。它内置于turtle其中,您始终可以组合许多折叠并使用该Control.Foldl材料使它们同时运行 - 这里

foldIO ::  Shell a -> FoldM IO a r -> IO r  -- specializing

无论如何,外壳都是秘密的FoldM IO a r -> IO r,所以这基本上是runShell. 要做到这一点,我们需要把正确的Shell和正确的结合起来FoldM IOFold a b包装中的和FoldM m a b类型的整个想法foldl是同时折叠。

我认为获得正确外壳的最简单方法就是让lstree折叠返回 aFilePath . testdir你基本上是这样写的:

withDirInfo :: FilePath -> Shell (Bool, FilePath)
withDirInfo tmp = do
    let lstmp = lstree tmp
    path <- lstmp
    bool <- liftIO $ testdir path
    return (bool, path)

所以现在我们可以Shell (Bool, FilePath)/tmpThis 中获取我们两个折叠所需的所有信息,因此我们的组合折叠将需要。

接下来我们可能会编写一个辅助折叠,将 的Text组件打印FilePath到给定的句柄:

sinkFilePaths :: Handle -> FoldM IO FilePath ()
sinkFilePaths handle = L.sink (T.hPutStrLn handle . format fp)

然后我们可以用它Handle -> FoldM IO FilePath ()来定义两个FoldM IO (Bool, FilePath) (). 每个都会将不同的内容写入不同的句柄,我们可以将它们合并为一个同时折叠的<*. 这是一个独立的FoldM IO ...,例如可以应用于类型的纯列表[(Bool, FilePath)]usingL.fold并且它将从列表中写入不同的东西到不同的句柄。但是,在我们的例子中,我们会将其应用于Shell (Bool, FilePath)我们定义的。

唯一微妙的部分是使用L.handlesM仅打印第二个元素,在这两种情况下,并且仅在另一种情况下过滤为目录的那些。这使用_2镜头和filtered镜头库。这可能会被简化,但看看你的想法:

{-#LANGUAGE OverloadedStrings #-}
import Turtle
import qualified Control.Foldl as L
import qualified System.IO as IO
import Control.Lens (_2,filtered)
import qualified Data.Text.IO as T

main = IO.withFile "tmpfiles.txt" IO.WriteMode $ \h ->
       IO.withFile "tmpdirs.txt" IO.WriteMode $ \h' -> do
        foldIO (withDirInfo "/tmp") (sinkFilesDirs h h') 

withDirInfo :: Turtle.FilePath -> Shell (Bool, Turtle.FilePath)
withDirInfo tmp = do
    let lstmp = lstree tmp
    path <- lstmp
    bool <- liftIO $ testdir path
    return (bool, path)

sinkFilePaths :: Handle -> FoldM IO Turtle.FilePath ()
sinkFilePaths handle = L.sink (T.hPutStrLn handle . format fp)

sinkFilesDirs  :: Handle -> Handle -> FoldM IO (Bool, Turtle.FilePath) ()
sinkFilesDirs h h' = allfiles <* alldirs where

  allfiles :: L.FoldM IO (Bool, Turtle.FilePath) ()
  allfiles = L.handlesM _2 (sinkFilePaths h)
  -- handle the second element of pairs with sinkFilePaths

  alldirs :: FoldM IO (Bool, Turtle.FilePath) ()
  alldirs = L.handlesM (filtered (\(bool,file) -> bool) . _2) (sinkFilePaths h')
 -- handle the second element of pairs where the first element
 -- is true using sinkFilePaths
于 2016-05-19T14:29:22.877 回答
0

听起来您正在寻找类似async从第一个外壳中分离外壳然后等待它们返回的东西。async是一个功能强大的库,可以实现比以下示例更多的功能,但它为您的要求提供了一个非常简单的解决方案:

import Control.Concurrent.Async
import Turtle.Shell
import Turtle.Prelude

main :: IO ()
main = do
  let lstmp1 = lstree "/tmp"
  let lstmp2 = lstree "/etc"
  view lstmp1
  view lstmp2
  job1 <- async $ view $ do
    path <- lstmp1
    x <- liftIO $ testdir path
    return x
  job2 <- async $ view $ do
    path <- lstmp2
    x <- liftIO $ testdir path
    return x
  wait job1
  wait job2

这是你要找的吗?

于 2016-05-19T13:37:14.517 回答