我将图片存储为非常大的List Int
,我想将它们变成List Color
但请记住 rgb 需要3个参数并且 rgba 需要4.所以让我们试试:
toColor : List Int -> List Color
toColor x =
if List.isEmpty x then
[]
else
List.append ( rgba <| List.take 4 x ) ( toColor <| List.drop 4 x )
这个定义是递归的。我们选择4个数字,创建一个rgb颜色并附加结果。但是,如果x
为List Int
,我们就无法写下:
rgba <| List.take 4 x
这是我们得到的一种错误。 rgb
期待三个号码,而不是列表
71| rgb <| List.take 4 x
^^^^^^^^^^^^^
(<|) is expecting the right argument to be a:
Int
But the right argument is:
List a
我想知道取出List Int
的第一个元素会返回Maybe Int
head : List a -> Maybe a
> head [1,2,3,4]
Just 1 : Maybe.Maybe number
以下是 rgb 的修改,将3 Maybe Int
转换为Color
。现在,阅读图像数据,我认为 rgba 是必要的,但我只需再添加一个。
rgb' : Maybe Int -> Maybe Int -> Maybe Int -> Color
rgb' a b c =
case a of
Nothing -> rgb 0 0 0
Just a' -> case b of
Nothing -> rgb 0 0 0
Just b' -> case c of
Nothing -> rgb 0 0 0
Just c' -> rgb a' b' c'
这是在我将这个d3js示例翻译成Elm时开始的,我注意到它使用了Elm目前不支持的一些功能。特别是ctx.getImageData()
,因为您无法导入和映像到Canvas。所以这是我的生产班制解决方案的一部分。
答案 0 :(得分:3)
在我看来,你正在寻找一种非常干净的方式
List Int
折叠为List (List Int)
,如果您想rgb
或rgba
,则每个子列表最多包含3个或4个成员。List Int
传递给一个函数,该函数将使用rgb
转换它,处理最终条目中没有足够Ints的情况。第一步,您可以编写一个名为groupList
的函数:
groupsOf : Int -> List a -> List (List a)
groupsOf size list =
let
group =
List.take size list
rest =
List.drop size list
in
if List.length group > 0 then
group :: groupsOf size rest
else
[]
您还可以使用elm-community/list-extra包中的greedyGroupsOf
功能获得此功能。
对于第二步,对列表值本身的结构进行模式匹配会更加清晰,而不是使用List.head
并匹配Maybe
。
rgbFromList : List Int -> Color
rgbFromList values =
case values of
r::g::b::[] ->
rgb r g b
_ ->
rgb 0 0 0
当列表中恰好有3个条目时,第一个案例将匹配,其他所有内容将落到将{0}交给rgb
。
您可以通过执行以下操作将所有这些内容放在一起:
toColor : List Int -> List Color
toColor x =
x
|> groupsOf 3
|> List.map rgbFromList
或者,如果您希望完全排除它们,而不是以rgb 0 0 0
结束无效颜色,则可以使用List.filterMap
功能删除您不想要的内容。我们可以将rgbFromList
更改为
rgbFromList : List Int -> Maybe Color
rgbFromList values =
case values of
r::g::b::[] ->
Just <| rgb r g b
_ ->
Nothing
然后将其称为
toColor : List Int -> List Color
toColor x =
x
|> groupsOf 3
|> List.filterMap rgbFromList
请注意,由于此处的groupsOf
函数以及elm-community / list-extra中的greedyGroupsOf
是递归的,因此对于非常大的列表,它将失败。
编辑:适用于非常大的列表
对于非常大的列表,很容易遇到递归问题。你最好的选择是折叠列表并在折叠时管理一些中间状态:
groupsOf : Int -> List a -> List (List a)
groupsOf size list =
let
update next state =
if (List.length state.current) == size then
{ current = [next], output = state.current :: state.output }
else
{ state | current = state.current ++ [next] }
result =
List.foldl update { current = [], output = [] } list
in
List.reverse (result.current :: result.output)
这可以通过折叠列表来实现,这是一个迭代过程而不是递归过程,并且一次构建一个组。最后一步反转列表,因为它将以相反的顺序构造,以便在输出列表开始变大时进行代价而不是进行昂贵的附加。我不希望这会溢出堆栈,但它很可能非常慢。在我看来,在合理的时间内获得所需结果的最佳选择是使用for循环在JavaScript中编写groupsOf函数,然后通过端口传递结果。
答案 1 :(得分:1)
这种递归实现应该在不构建堆栈的情况下工作,因为Elm具有尾部调用优化。每个步骤从原始列表中取三个整数,并将它们附加到颜色列表中。当原始列表中的元素少于三个时,递归停止并返回颜色列表。
它使用rgb,但可以轻松修改以从列表中获取4个元素。
它也会反转顺序,因此您可能需要将它与List.reverse结合使用。
import Color exposing (rgb, Color)
listColors : List Int -> List Color
listColors =
listColors' []
listColors' : List Color -> List Int -> List Color
listColors' colors ints =
case ints of
r :: g :: b :: rest ->
listColors' (rgb r g b :: colors) rest
_ ->
colors