【问题标题】:type tetris - stuck mixing monadic and pure code类型俄罗斯方块 - 卡住混合单子和纯代码
【发布时间】:2020-05-05 16:18:20
【问题描述】:

我正在尝试在 haskell 中实现一些 vulkan-tutorial。

现在我坚持尝试从 c: 翻译这段代码

for (const char* layerName : validationLayers) {
    bool layerFound = false;

    for (const auto& layerProperties : availableLayers) {
        if (strcmp(layerName, layerProperties.layerName) == 0) {
            layerFound = true;
            break;
        }
    }

    if (!layerFound) {
        return false;
    }
}

return true; 

到目前为止,我已经到了这一点:

-- This has type (Int -> Text) -> Bool
let partOne = all (`elem` requiredValidationLayers) . flip map [0 .. realCount-1]
-- This has type Int -> IO Text
let partTwo i = do
        let layerProperty = advancePtr layerProperties i
        myField_ <- readStringField @"layerName" layerProperty
        pure $ toS myField_ :: IO Text

我觉得我已经拥有了所有的东西,但我也可能会朝着完全错误的方向前进。

我如何把这些东西放在一起?

谢谢

PS:好的,我只是注意到设置包含检查可能会被反转 - 没关系,为了这个问题,让我们假装它实际上很好

【问题讨论】:

  • partTwo 到底在哪里?原始代码 C 代码应该做什么?
  • partTwo 接受一个整数 i 并在 c 中返回 layerProperties[i].layerName 等价物,因为 vulkan-api 如何处理这些构造,layerProperties 实际上是一个不透明的 Ptr结构数组。因此所有这些样板
  • 原始c代码是一个集合包含检查:如果validationLayers的每个元素都在availableLayers中
  • 写一个pure函数f实现C代码。然后使用Control.Appicative.liftA2 将其提升以使用IO 操作中的值。也就是说,如果f :: [a] -&gt; [b] -&gt; Bool,那么liftA2 f :: IO [a] -&gt; IO [b] -&gt; IO Bool(特化为IO,即)。
  • 除了其中一个Lists只能在IO Monad中读取,所以实际上是[Layer] -> IO [Layer] -> IO Bool。仍然举起它应该会产生类似 IO [Layer] -> IO [Layer] -> IO Bool 的东西,对吧?由于 monads 的可连接性......我稍后会以全新的心态再次查看这段代码

标签: c haskell monads code-translation do-notation


【解决方案1】:

感谢所有评论者,我想我现在明白了:)

这是它的样子(在 IO ()do 块内):

supportedLayers <- for [0 .. realCount-1] $ \i -> do
    let layerProperty = advancePtr layerProperties i
    myField_ <- readStringField @"layerName" layerProperty
    pure $ toS myField_ :: IO Text
return $ requiredValidationLayers `includes` supportedLayers

在哪里

includes :: Eq a => [a] -> [a] -> Bool
includes a b = all (`elem` b) a

【讨论】:

  • 我有点希望有人实际上解释了这段代码有什么问题,而不是投反对票?
【解决方案2】:

我将从一些假设开始:

  1. 你有一个类型data Layer = ...
  2. 你有一个类型别名type Name = String
  3. 你有一些功能layerName :: Layer -&gt; Name

我将介绍纯函数的定义,不是因为它非常重要,而是因为它使用一种技术将纯代码转换为不纯(即IO-依赖)代码。


给定的层有给定的名称吗?

首先,你有基础操作

strcmp(layerName, layerProperties.layerName) == 0

这是一个简单的 Haskell 函数,它接受 NameLayer 并在层具有给定名称时返回 True

hasName :: Name -> Layer -> Bool
hasName name layer = name == layerName layer

多个层之一是否具有多个名称中的任何一个?

但我们没有单一的名称或层;我们有每个列表。我们可以通过使用列表的Applicative 实例来对不确定性进行建模,从而轻松处理此问题。

import Control.Applicative

foo :: [Name] -> [Layer] -> [Bool]
foo = liftA2 hasName

我不会费心给这个函数起个好名字,因为我们不需要。

现在,我们可以得到一个比较 every 名称与 every 层的结果列表,但我们不关心单个结果:我们只想要 one 表示我们是否找到匹配项的值。为此,我们将使用or :: [Bool] -&gt; Bool

findMatch :: [Name] -> [Layer] -> Bool
findMatch names layers = or (foo names layers)

foo 很简单,我们只需内联它的定义,这样我们就不需要想出更好的名字了:

findMatch :: [Name] -> [Layer] -> Bool
findMatch names layers = or (liftA2 hasName names layers)

处理 IO

现在,我们一直假设我们已经将名称和层列表作为纯值。但如果我们不这样做呢?如果我们只有从文件或其他地方读取的列表,那么我们有IO [Name] 和/或IO [Layer] 怎么办?解决方案是再次使用liftA2,但我们将使用IO 实例,而不是使用Applicative 的列表实例。

ionames :: IO [Name]
ionames = ...

iolayers :: IO [Layer]
iolayers = ...

result :: IO Bool
result = liftA2 findMatch ionames iolayers

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-12-30
    • 1970-01-01
    • 2013-03-17
    • 1970-01-01
    • 1970-01-01
    • 2018-06-06
    • 1970-01-01
    相关资源
    最近更新 更多