递归Haskell函数,用于确定元素在列表中的位置
我有一段代码,它将返回字符数组中字符的索引,但是如果数组中没有值,我希望我的函数返回类似-1的值。如果元素不在数组中,函数将返回数组的大小。关于如何更改代码以应用此功能,您有什么想法吗 我尽量不使用任何花哨的函数来实现这一点。我只想要没有内置函数的简单代码递归Haskell函数,用于确定元素在列表中的位置,haskell,recursion,Haskell,Recursion,我有一段代码,它将返回字符数组中字符的索引,但是如果数组中没有值,我希望我的函数返回类似-1的值。如果元素不在数组中,函数将返回数组的大小。关于如何更改代码以应用此功能,您有什么想法吗 我尽量不使用任何花哨的函数来实现这一点。我只想要没有内置函数的简单代码 isPartOf :: [(Char)] -> (Char) -> Int isPartOf [] a = 0 isPartOf (a:b) c | a == c = 0 | otherwise = 1 + isP
isPartOf :: [(Char)] -> (Char) -> Int
isPartOf [] a = 0
isPartOf (a:b) c
| a == c = 0
| otherwise = 1 + isPartOf b c
例如:
*Main> isPartOf [('a'),('b'),('c')] ('z')
3
但我想:
*Main> isPartOf [('a'),('b'),('c')] ('z')
-1
让我们尝试定义这样一个函数,但是如果元素不是列表的一部分,我们不能返回-1,而是什么也不返回: 所以,它是这样工作的:
>> isPartOf [('a'),('b'),('c')] ('z')
Nothing
it :: Maybe Int
>> isPartOf [('a'),('b'),('c')] ('c')
Just 2
it :: Maybe Int
之后,我们可以使用内置函数将Nothing大小写转换为-1:
如果您对已经存在这样一个函数感到好奇,您可以使用Hoogle搜索[a]->a->Maybe Int函数:
第一个答案是:
希望这有帮助。让我们尝试定义这样一个函数,但是如果元素不在列表中,我们不能返回-1,而不能返回任何内容: 所以,它是这样工作的:
>> isPartOf [('a'),('b'),('c')] ('z')
Nothing
it :: Maybe Int
>> isPartOf [('a'),('b'),('c')] ('c')
Just 2
it :: Maybe Int
之后,我们可以使用内置函数将Nothing大小写转换为-1:
如果您对已经存在这样一个函数感到好奇,您可以使用Hoogle搜索[a]->a->Maybe Int函数:
第一个答案是:
希望这有帮助。如果您只需将当前元素idx传递给下一个递归,就可以轻松实现:
isPartOf :: [Char] -> Char -> Int
isPartOf lst c = isPartOf' lst c 0
isPartOf' :: [Char] -> Char -> Int -> Int
isPartOf' [] a _ = -1
isPartOf' (a:b) c idx
| a == c = idx
| otherwise = isPartOf' b c (idx + 1)
只要将当前元素idx传递给下一个递归,就可以轻松实现:
isPartOf :: [Char] -> Char -> Int
isPartOf lst c = isPartOf' lst c 0
isPartOf' :: [Char] -> Char -> Int -> Int
isPartOf' [] a _ = -1
isPartOf' (a:b) c idx
| a == c = idx
| otherwise = isPartOf' b c (idx + 1)
实现这一目标的最小变化是
isPartOf :: [Char] -> Char -> Int
isPartOf [] a = (-1) -- was: 0
isPartOf (a:b) c
| a == c = 0
| otherwise = 1 + -- was: isPartOf b c
if (isPartOf b c) < 0 then (-2) else (isPartOf b c)
那-2看起来特别特别!使用guards的更紧凑、更惯用的版本也将允许我们解决这一问题:
isPartOf :: Eq a => [a] -> a -> Int
isPartOf [] a = (-1)
isPartOf (a:b) c
| a == c = 0
| d < 0 = d
| otherwise = 1 + d
where
d = isPartOf b c
这就是我们正在使用的。但当我们刚开始学习哈斯克尔时,这可能被视为一种幻想
当您编写了更多类似的函数,并且厌倦了一次又一次地手动重复相同的模式时,您将开始欣赏它并希望使用它。但只有到那时
还有一个问题是,我们的代码在从递归的基本情况返回的过程中计算其结果
但是它可以计算它,朝着它,所以当找到匹配的字符时,它可以立即返回它。如果找到了列表的末尾,则放弃到目前为止计算的结果,并返回-1。这就是我们所采取的方法
尽管创建一个附加函数会占用全局名称空间。通常通过在所谓的worker/wrapper转换中对其进行内部定义来实现这一点:
isPartOf :: Eq a => [a] -> a -> Int
isPartOf xs c = go xs 0
where
go [] i = (-1)
go (a:b) i
| a == c = i
| otherwise = -- go b (1 + i)
go b $! (1 + i)
另外一个好处是,我们不需要传递未更改的值c—从内部工作函数go的角度来看,它在外部范围内可用,由我们的函数isPartOf包装并仅可访问
美元!是一个特殊的调用运算符,它确保立即计算其参数值,而不是延迟。这消除了不必要的惰性,并进一步提高了代码效率
但从设计整体清洁度的角度来看,最好返回包装在a中的索引i,也就是说,仅i或无,而不是使用一个毕竟不那么特殊的特殊值——它仍然是一个Int
让类型反映我们的意图是很好的,也许Int可以清楚而清晰地表达它,所以我们不必记住哪些值是特殊的,哪些是规则的,这样这些知识就不是程序文本的外部知识,而是程序文本的固有知识
这是一个小而简单的更改,结合了前两个变体的最佳部分:
isPartOf :: Eq a => [a] -> a -> Maybe Int
isPartOf .....
.......
....... Nothing .....
.......
....... Just i .....
.......
所有代码都没有经过测试。如果有错误,我们会邀请您查找并更正错误,并通过测试进行验证。实现这一点的最小更改是
isPartOf :: [Char] -> Char -> Int
isPartOf [] a = (-1) -- was: 0
isPartOf (a:b) c
| a == c = 0
| otherwise = 1 + -- was: isPartOf b c
if (isPartOf b c) < 0 then (-2) else (isPartOf b c)
那-2看起来特别特别!使用guards的更紧凑、更惯用的版本也将允许我们解决这一问题:
isPartOf :: Eq a => [a] -> a -> Int
isPartOf [] a = (-1)
isPartOf (a:b) c
| a == c = 0
| d < 0 = d
| otherwise = 1 + d
where
d = isPartOf b c
这就是我们正在使用的。但当我们刚开始学习哈斯克尔时,这可能被视为一种幻想
当您编写了更多类似的函数,并且厌倦了一次又一次地手动重复相同的模式时,您将开始欣赏它并希望使用它。但只有到那时
还有一个问题是,我们的代码在从递归的基本情况返回的过程中计算其结果
但是它可以计算它,朝着它,所以当找到匹配的字符时,它可以立即返回它。如果找到了列表的末尾,则放弃到目前为止计算的结果,并返回-1。这就是我们所采取的方法
尽管创建一个附加函数会占用全局名称空间。通常通过在所谓的worker/wrapper转换中对其进行内部定义来实现这一点:
isPartOf :: Eq a => [a] -> a -> Int
isPartOf xs c = go xs 0
where
go [] i = (-1)
go (a:b) i
| a == c = i
| otherwise = -- go b (1 + i)
go b $! (1 + i)
另一个好处是,我们不需要传递不变的值c——从内部工作者的角度来看,它在外部范围内可用
Action go,由我们的功能isPartOf包装并仅可访问
美元!是一个特殊的调用运算符,它确保立即计算其参数值,而不是延迟。这消除了不必要的惰性,并进一步提高了代码效率
但从设计整体清洁度的角度来看,最好返回包装在a中的索引i,也就是说,仅i或无,而不是使用一个毕竟不那么特殊的特殊值——它仍然是一个Int
让类型反映我们的意图是很好的,也许Int可以清楚而清晰地表达它,所以我们不必记住哪些值是特殊的,哪些是规则的,这样这些知识就不是程序文本的外部知识,而是程序文本的固有知识
这是一个小而简单的更改,结合了前两个变体的最佳部分:
isPartOf :: Eq a => [a] -> a -> Maybe Int
isPartOf .....
.......
....... Nothing .....
.......
....... Just i .....
.......
所有代码都没有经过测试。如果有错误,我们会邀请您查找并更正错误,并通过测试进行验证。您将函数用作累加器。这是很酷的,除了添加负一。累加器不能从累加切换到提供负1。您需要从函数累加器中获得两种不同的内容。您可以使用计数器进行一件事,如果由于未找到匹配项而导致计数变得不必要,则会发出负1,并且不会丢失任何内容。计数将是另一个参数。啊。你可以用Maybe,但这会让事情复杂化。上面的两个函数更简单。这里有两个函数。第一个是你的,但累加器不是加法的,而是串联的
cIn (x:xs) c | x == c = [1]
| null xs = [-1]
| otherwise = 1:cIn xs c
Cin ['a','b','c'] 'c'
[1,1,1]
cIn ['a','b','c'] 'x'
[1,1,-1]
第二个函数是
f ls = if last ls == 1 then sum ls else -1
会的
f $ Cin ['a','b','c'] 'c'
三,
及
-一,
您可以通过将[1]更改为[0]将函数用作累加器来将索引基数归零。这是很酷的,除了添加负一。累加器不能从累加切换到提供负1。您需要从函数累加器中获得两种不同的内容。您可以使用计数器进行一件事,如果由于未找到匹配项而导致计数变得不必要,则会发出负1,并且不会丢失任何内容。计数将是另一个参数。啊。你可以用Maybe,但这会让事情复杂化。上面的两个函数更简单。这里有两个函数。第一个是你的,但累加器不是加法的,而是串联的
cIn (x:xs) c | x == c = [1]
| null xs = [-1]
| otherwise = 1:cIn xs c
Cin ['a','b','c'] 'c'
[1,1,1]
cIn ['a','b','c'] 'x'
[1,1,-1]
第二个函数是
f ls = if last ls == 1 then sum ls else -1
会的
f $ Cin ['a','b','c'] 'c'
三,
及
-一,
您可以通过将[1]更改为[0]==和+是内置函数。==和+是内置函数。这种方法比另一个答案好,但您应该解释原因。这种方法比另一个答案好,但您应该解释原因。