移动列表中特定元素的简单函数

2024-05-04

我是 Haskell 的新手,我正在尝试弄清楚如何创建一个函数:

    shift:: Eq a => a -> [a] -> Int -> [a]
    shift x (h:t) z

输入:一个通用列表和一个相同类型的元素 x

前提条件:元素x存在于列表中

Output:

如果 n

如果 n > 0,则将 x 右移 n 直到到达列表的最后一个

如果n == 0,则从列表中删除x(总是谈论x的第一次出现,它可能在列表中显示超过1次)。

请记住,我已经完成了删除功能

    delete :: Eq b => b -> [b] -> [b]

找到所需元素的第一次出现并将其删除。我想我可以在班次内使用删除。任何形式的帮助表示赞赏。

[一些 I/O 示例]

     Main> shift 3 [] 5
     []
     Main> shift 0 [1,2,3,4] 4
     [1,2,3,4]
     Main> shift 3 [1,2,3,4,3,2,1] 3
     [1,2,4,3,2,3,1]
     Main> shift 6 [1,2,3,4,5,6,7,8] 7
     [1,2,3,4,5,7,8,6]
     Main> shift 'k' "skin" 0
     "sin"
     Main> shift '.' "factor.bit." (-2)
     "fact.orbit."
     Main> shift '.' "0000.1111.00" (-10)
     ".00001111.00"
     Main> shift "one" ["one", "two", "three"] (-2)
     ["one","two","three"]
     Main> shift "one" ["one", "two", "three"] 1
     ["two","one","three"]
     Main> shift "three" ["one", "two", "three"] 5
     ["one","two","three"]

正如 @WillemVanOnsem 所建议的,您可能想尝试编写一个将目标元素向右移动一格的函数。即使这个简化的问题也可能非常具有挑战性!

看看是否可以实现直接递归版本。它的结构可能与您的类似delete函数,除非它会swap两个元素,而不是在关键点删除一个元素。 (答案在底部——寻找定义simpleShiftRight.)

完成此操作后,请尝试使用这种替代方法,该方法的优点是更容易推广到解决您的原始问题。

首先,使用delete不是很有帮助,因为delete“忘记”元素原来在哪里。例如,以下两者:

delete '.' "abc.def"
delete '.' "abcde.f"

yield "abcdef",并且尚不清楚如何使用此结果,例如将句点位置向右移动一个位置。

相反,您真正想做的是将字符串分成目标元素之前和之后的部分。也就是说,你想定义一个函数split其工作原理如下:

> split '.' "abc.def"
("abc","def")
> split '.' "abcde.f"
("abcde","f")

有了这个结果,改变周期就变得容易多了。

例如,如果我们想将周期向右移动一位,我们可以首先定义一个函数

pairRight :: ([a], [a]) -> ([a], [a])

其工作原理如下:

> pairRight ("abc","def")
("abcd","ef")
> pairRight ("abcde","f")
("abcdef","")

和一个函数

rejoin :: a -> ([a], [a]) -> [a]

其工作原理如下:

> rejoin '.' ("abcd","ef")
"abcd.ef"
> rejoin '.' ("abcdef","")
"abcdef."

并将它们组合起来:

> rejoin '.' (pairRight (split '.' "abc.def"))
"abcd.ef"
> rejoin '.' (pairRight (split '.' "abcde.f"))
"abcdef."

得到一个将字符向右移动一位的函数。

Now, split可以根据库函数来定义break,像这样:

split :: Eq a => a -> [a] -> ([a], [a])
split x xs = let (a, _:b) = break (==x) xs in (a,b)

能实现一下功能吗pairRight and rejoin?它们不应该太难,但如果你遇到困难,答案就在底部。

您可能还想尝试定义split从头开始而不使用break。这是一个有点棘手的递归函数。如果您从“明显”的方法开始:

split :: Eq a => a -> [a] -> ([a], [a])
split x (y:ys) | x == y    = (..., ys)
               | otherwise = split x ys
split _ [] = error "split: target not found"

你会遇到问题。不清楚如何填写...因为你在递归中丢弃了列表的开头。希望您已经了解到解决此问题的一种方法是引入一个额外的参数来跟踪已处理的列表元素并定义一个函数:

split' :: Eq a => a -> [a] -> [a] -> ([a], [a])
split' x ls (r:rs) = ...

where x是我们要寻找的元素,ls是我们已经处理过的列表左侧的元素集(其中我们没有找到x), and (r:rs)是我们仍在处理的列表的右侧。

如果您需要进一步的提示,这里有一个模板:

split' x ls (r:rs) | x == r    = (..., ...)
                   | otherwise = split' x (...) rs
split' _ _ [] = error "split: target not found"

可以填写一下吗...这里?如果可以的话,您可以定义:

split :: Eq a => a -> [a] -> ([a], [a])
split x xs = split' x [] xs

一旦你有split, pairRight, and rejoin定义后,您应该能够将它们组合成一个函数:

shiftRight :: Eq a => a -> [a] -> [a]

可以将目标元素向右移动一位。

如果你遇到困难,这里有一个完整的定义shiftRight和它的 帮手:

shiftRight :: (Eq a) => a -> [a] -> [a]
shiftRight x xs = rejoin x (pairRight (split x xs))

-- alternative definition of split:
-- split :: Eq a => a -> [a] -> ([a], [a])
-- split x xs = let (a, _:b) = break (==x) xs in (a,b)

split :: Eq a => a -> [a] -> ([a], [a])
split x xs = split' x [] xs

split' :: Eq a => a -> [a] -> [a] -> ([a], [a])
split' x ls (r:rs) | x == r    = (ls, rs)
           | otherwise = split' x (ls ++ [r]) rs
split' _ _ [] = error "split: target not found"

pairRight :: ([a], [a]) -> ([a], [a])
pairRight (ls, r:rs) = (ls ++ [r], rs)

rejoin :: a -> ([a], [a]) -> [a]
rejoin x (ls, rs) = ls ++ [x] ++ rs

在这个版本中,尝试shiftRight不在列表中或已经位于最右边位置的目标将给出错误。您可能想尝试解决这个问题。 (注意split可以返回一个Maybe [a],产生Nothing如果没有找到目标;修改起来也不应该太困难pairRight因此,如果该对已经尽可能向右移动,则它不会执行任何操作。)

如果这对于一个简单的问题来说似乎很麻烦,我想确实如此。在实际代码中,经验丰富的 Haskell 程序员可能会编写直接递归版本:

simpleShiftRight :: (Eq a) => a -> [a] -> [a]
simpleShiftRight x (y:z:rest) | x == y    = z:y:rest
                              | otherwise = y : simpleShiftRight x (z:rest)
simpleShiftRight _ rest                   = rest

或者这个使用break:

simpleShiftRight :: (Eq a) => a -> [a] -> [a]
simpleShiftRight x xs = case break (==x) xs of
  (ls, y:z:rs) -> ls ++ z:y:rs
  _ -> xs

两个版本都很简洁,处理所有极端情况,并且“显然是正确的”。正如前面提到的,缺点是这个版本不太容易推广到您的原始问题。

上面的版本确实很容易概括——你只需要替换pairRight具有更复杂的配对移位功能。例如,定义:

pairRightN :: Int -> ([a],[a]) -> ([a],[a])
pairRightN n (ls, r:rs) | n > 0 = pairRightN (n-1) (ls ++ [r], rs)
pairRightN _ (ls, rs)           = (ls, rs)

允许您处理所有正值n(即,无论有多大,都可以)。进一步将其推广到处理左移也不是太难。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

移动列表中特定元素的简单函数 的相关文章

随机推荐

  • Xamarin 中 QR 扫描后的处理对话框

    我在Xamarin应用程序中使用QR码扫描仪 当它扫描QR码时 它会执行一些操作 大约需要一分钟 而在执行操作时 我想在屏幕上显示一个加载对话框 但是 它没有显示在屏幕上 并且在应用程序的其他地方 它运行得很好 Code var expec
  • 如何配置Android AccessibilityService

    我正在研究AndroidAccessibilityService想要查看所有可能发生的事件类型 手势和关键事件 我能够收到所有public void onAccessibilityEvent final AccessibilityEvent
  • Fabric.js 如何在不拉伸文本的情况下水平调整 IText 大小

    我在父 Group 对象中有这个 IText 对象 当我选择组并水平 以及垂直 调整其大小时 IText 也会调整大小 这使得文本拉伸并且看起来很糟糕 现在我想做的是将 IText 中心本身 保持其纵横比 放在组内 我怎样才能做到这一点 我
  • 关于合并排序代码中的组合步骤的困惑

    我有一个关于数组上的合并排序如何工作的问题 我理解 划分 步骤 它将输入数组划分为 1 长度的元素 然而 当谈到 合并 部分 组合步骤 时 我感到困惑 例如 给定输入 3 5 1 8 2 除法过程将产生 5 个元素 3 5 1 8 2 我只
  • 在 Swift 3 中从 UUID 获取数据

    我用 Objective C 编写了以下代码 我试图在 Swift 3 中使用它 一些等效函数似乎在 Swift 3 中不可用 下面的代码是 Objective C 中的代码 NSUUID vendorIdentifier UIDevice
  • 适用于 Angular 2+ 的具有多个日期选择的日历

    我需要显示一个日历并让用户选择多个日期 例如2017 年 1 月 2 日 2017 年 1 月 3 日 2017 年 1 月 4 日 也就是说 不是一个范围 而是多个日期 在 Angular 1 x 中 我使用了gm datepickerM
  • PHP - Paypal API 表单和安全性 [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 我在我的电子商务应用程序上使用标准 php paypal 表单进行付款 我注意到只有 firebug 的人可以在通过 立即付款 按钮发
  • 如何切换到新数据库

    我想将我的 django 项目部署到生产环境 并将其与一个新的空数据库关联 我做了如下操作 创建一个新的空数据库 更新了settings py并将数据库名称指向新的数据库名称 删除了我的应用程序下的migrations文件夹 运行 pyth
  • 在sql server中透视固定的多列表

    我有一个需要为报告服务进行旋转的表格 DateCreated Rands Units Average Price Success Unique Users 2013 08 26 0 0 0 0 0 2013 08 27 0 0 0 0 0
  • Microsoft Graph API 中的一个或多个属性包含无效值

    我想在 Azure Active Directory B2C 上创建用户 我按照给定链接中的每个步骤进行操作Here https learn microsoft com en us azure active directory b2c ac
  • 如何重定向到外部404页面Python Flask

    我正在尝试将 404 重定向到外部 URL 如下所示 app route 404 def http error handler error return flask redirect http www exemple com 404 404
  • 将 vbCrLf 应用于文本框的内容

    我在 Excel vba 项目中有一个用户窗体 在设计时它是空的 在表单初始化事件中 我有以下代码 Private Sub UserForm Initialize txtSQL value SELECT MyName ColY vbCrLf
  • 在 gridLayout 中从右向左放置项目

    我有一个GridLayout在我的其中一个布局中 我想从右到左放置项目 这意味着我希望将单元格 1 1 放在布局的右上角 我已经测试了这些代码GridView so far 1 android gravity right and andro
  • 如何在 php 数组中添加条件?

    这是数组 anArray array theFirstItem gt a first item if True conditionalItem gt it may appear base on the condition theLastIt
  • 未初始化成员的警告在 C++11 上消失

    我编译这个简单的程序 include
  • 使用回溯(而不是 DFS)背后的直觉

    我正在解决单词搜索 https leetcode com problems word search description LeetCode com 上的问题 给定一个 2D 板和一个单词 查找该单词是否存在于网格中 该单词可以由顺序相邻单
  • 使用 swift 在 WKWebView 上显示活动指示器

    我正在处理以下代码 并尝试在页面加载时在视图中显示活动指示器 我尝试实施WKNavigationDelegate方法 但我失败了 因为没有任何显示 对于如何解决这个问题 有任何的建议吗 我没有设置 SupportWebView 视图dele
  • 在哪里存储字符串值?在 strings.xml 中还是在常量类中?

    在android中 我们可以将字符串值存储在strings xml文件中或某些常量类中作为静态最终变量 在某些情况下是否有理由选择一个而不是另一个 简而言之 代码中使用的值 始终使用常量类 优点 代码保持集成 并且您的包可以在其他项目 上下
  • CPU缓存:两个地址之间的距离是否需要小于8字节才能具有缓存优势?

    这似乎是一个奇怪的问题 假设缓存行的大小为 64 字节 此外 假设 L1 L2 L3 具有相同的缓存行大小 this https stackoverflow com a 15333156 8385554帖子说英特尔酷睿 i7 就是这种情况
  • 移动列表中特定元素的简单函数

    我是 Haskell 的新手 我正在尝试弄清楚如何创建一个函数 shift Eq a gt a gt a gt Int gt a shift x h t z 输入 一个通用列表和一个相同类型的元素 x 前提条件 元素x存在于列表中 Outp