Scala 一个接一个地滑?

Scala 一个接一个地滑?,scala,collections,functional-programming,Scala,Collections,Functional Programming,不通过索引处理集合的优点之一是避免了一个接一个的错误。这当然不是唯一的优势,但也是其中之一 现在,我经常在Scala中的一些算法中使用滑动,但我觉得它通常会导致类似于off by one错误的结果,因为在一个sizen集合中m元素的滑动具有sizen-m+1元素。或者,更简单地说,列表滑动2比列表短一个元素 我的感觉是,在这个模式中缺少了一个抽象,一部分是滑动的,另一部分是更多的——像折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠

不通过索引处理集合的优点之一是避免了一个接一个的错误。这当然不是唯一的优势,但也是其中之一

现在,我经常在Scala中的一些算法中使用
滑动
,但我觉得它通常会导致类似于off by one错误的结果,因为在一个size
n
集合中
m
元素的
滑动
具有size
n-m+1
元素。或者,更简单地说,
列表滑动2
列表
短一个元素

我的感觉是,在这个模式中缺少了一个抽象,一部分是滑动的,另一部分是更多的——像折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠折叠。然而,我想不出那可能是什么。有人能帮我找到启示吗

更新

既然人们不清楚我在说什么,让我们来考虑一下这个例子。我想把字符串大写。基本上,每一个前面没有字母的字母都应该是大写,而所有其他字母都应该是小写。使用
滑动
,我必须对第一个字母或最后一个字母进行特殊处理。例如:

def capitalize(s: String) = s(0).toUpper +: s.toSeq.sliding(2).map {
  case Seq(c1, c2) if c2.isLetter => if (c1.isLetter) c2.toLower else c2.toUpper
  case Seq(_, x) => x
}.mkString

在python/R/Matlab中,我经常遇到这个问题,在这里,你需要diff()一个向量,然后不能将它与原来的向量对齐!这是非常令人沮丧的

我认为真正缺少的是,向量只包含因变量,并假设程序员跟踪自变量,即集合所覆盖的维度

我认为解决这个问题的方法是让语言在某种程度上跟踪自变量;可能是静态地遍历类型,或者是动态地将它们与向量一起存储。然后它可以检查独立的轴,确保它们对齐,或者,我不知道这是否可行,将东西移动以使它们对齐

这是迄今为止我想到的最好的一次

编辑

另一种思考方式是,为什么你的收藏有秩序?为什么不只是一套?顺序意味着什么,但集合并没有跟踪它——它基本上是使用顺序位置(与数字索引一样信息丰富)来代表真正的意义

编辑

另一个结果是像滑动这样的变换实际上代表了两种变换,一种是因变量,另一种是它们的轴

当您想要对列表应用一个简单的
diff()
时,可以将其视为等同于以下矩阵乘法

a = (0 1 4 3).T

M = ( 1 -1  0  0)
    ( 0  1 -1  0)
    ( 0  0  1 -1)

diff(a) = M * a = (1 3 1).T
现在,如果我们替换加法和乘法(如果我们将矩阵M中的数字推广),我们可以对一般列表操作使用相同的方案

因此,加号是一个列表附加操作(之后是
展平
,或者只是一个
收集
操作),乘法等价物是
Some(41;
None
,窗口大小为2的幻灯片变成:

M = (Some(_) Some(_) None None)
    (None Some(_) Some(_) None)
    (None None Some(_) Some(_))

slide(a) = M “*” a = ((0 1) (1 4) (4 3)).T
不确定,如果这是您正在寻找的抽象类型,但它将是一类改变项目数量的操作的泛化

diff
slide
长度为n的输入的m阶操作将需要使用大小为n-m+1×n的矩阵


编辑:解决方案可以是将
列表[A]
转换为
列表[A]]
,然后用
预先添加或附加(
slideLeft
slideRight
)这些。这样,您就可以处理
map
方法中的所有魔法

list.slideLeft(2) {
  case Seq(Some(c1), Some(c2)) if c2.isLetter => if (c1.isLetter) c2.toLower else c2.toUpper
  case Seq(_, Some(x)) => x
}

Off by one错误表明您试图将原始列表与滑动列表一一对应,但由于滑动列表的元素较少,因此出现了一些奇怪的情况

您的示例的问题陈述可以大致表述为:“如果(a)是第一个字符,或者(b)紧跟字母字符,则每个字符都大写”。正如欧文指出的,第一个字符是一个特例,任何抽象都应该尊重这一点。这是一种可能性

def slidingPairMap[A, B](s: List[A], f1: A => B, f2: (A, A) => B): List[B] = s match {
  case Nil => Nil
  case x :: _ => f1(x) +: s.sliding(2).toList.map { case List(x, y) => f2(x, y) } 
}
(这不是最好的实现,但你知道了)。这推广到滑动三元组,有两个错误,等等。
slidengpairmap
的类型清楚地表明正在制作特殊的套管

可以使用等效的签名

def slidingPairMap[A, B](s: List[A], f: Either[A, (A, A)] => B): List[B]
然后
f
可以使用模式匹配来确定它是在处理第一个元素,还是在处理后续元素


或者,正如Owen在评论中所说,为什么不做一个修改的
滑动
方法,给出元素是否为第一个元素的信息

def slidingPairs[A](s: List[A]): List[Either[A, (A, A)]]

我猜最后一个想法与Debilski在评论中的建议是一致的:用None填充列表的开头,用Some
包装所有现有元素,然后调用
滑动
,你所描述的“一个关”问题提醒了我数字信号处理中的边界条件问题。由于数据(列表)是有限的,所以出现了问题。无限数据(流)不会出现这种情况。在数字信号处理中,通过将有限信号扩展到无限信号来解决这些问题。这可以通过多种方式完成,例如重复数据或重复数据并在每次重复时将其反转(就像离散余弦变换一样)

借用这些方法进行滑动将导致一个抽象,它不会表现出一个问题所带来的偏差:

(1::2::3::Nil).sliding(2)
会让步

(1,2), (2,3), (3,1)
对于圆形边界条件和

(1,2), (2,3), (3,2)

对于具有反转的圆形边界条件

在映射了
Some(41;
元素之后,我会预先设置None;请注意,显而易见的方法是(在默认情况下匹配两个
Some
,如在“编辑方式”中所做的那样)
((None +: "foo1bar".toSeq.map(Some(_))) sliding 2).map {
   case Seq(c1Opt, Some(c2)) if c2.isLetter => if (c1Opt.map(_.isLetter).getOrElse(false)) c2.toLower else c2.toUpper
   case Seq(_, Some(x)) => x
}.mkString
res13: String = "Foo1Bar"
def capitalize2(s: String) = (("", true) /: s){ case ((res, notLetter), c) => 
  (res + (if (notLetter) c.toUpper else c.toLower), !c.isLetter)
}._1
initializedSliding(init: List[A]) = (init ::: this).sliding(1 + init.length)
finalizedSliding(tail: List[A]) = (this ::: tail).sliding(1 + tail.length)
List(1, 2, 3, 4, 5)
List(0, 0, 1), List(0, 1, 2), List(1, 2, 3), List(2, 3, 4), List(3, 4, 5)
List(1, 2, 3), List(2, 3, 4), List(3, 4, 5), List(4, 5, 0), List(5, 0, 0)
   str=. 'now  is  the    time'    NB. Example w/extra spaces for interest
   ]whspc=. ' '=str                NB. Mark where spaces are=1
0 0 0 1 1 0 0 1 1 0 0 0 1 1 1 1 0 0 0 0
   ]tt=. #:i.4                     NB. Truth table
0 0
0 1
1 0
1 1
   (*.-.)/"1 tt                    NB. Apply to 1-D sub-arrays (rows)
0 0 1 0                            NB. As hoped.
   2(*.-.)/\whspc                  NB. Apply to 2-ples
0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 1 0 0 0
   #whspc
20
   #1,2(*.-.)/\whspc
20
   1,2(*.-.)/\whspc
1 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 1 0 0 0
   'lc uc'=. 'abcdefghijklmnopqrstuvwxyz';'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
   (uc,' '){~lc i. str
NOW  IS  THE    TIME
       (1,2(*.-.)/\whspc) } str,:(uc,' '){~lc i. str
Now  Is  The    Time
   (1,2(*.-.)/\' '=str) } str,:(uc,' '){~lc i. str
Now  Is  The    Time
def slidingFoldLeft[A, B] (seq: Seq[A], window: Int)(acc: B)(
    f: (B, Seq[A]) => B): B = {
  if (window > 0) {
    val iter = seq.sliding(window)
    iter.foldLeft(acc){
      // Operate normally
      case (acc, next) if iter.hasNext => f(acc, next)
      // It's at the last <window> elements of the seq, handle current case and 
      // call recursively with smaller window
      case (acc, next) =>
        slidingFoldLeft(next.tail, window - 1)(f(acc, next))(f)
    }
  } else acc
}

def capitalizeAndQuestionIncredulously(s: String) =
  slidingFoldLeft(s.toSeq, 2)("" + s(0).toUpper) {
    // Normal iteration
    case (acc, Seq(c1, c2)) if c1.isLetter && c2.isLetter => acc + c2.toLower
    case (acc, Seq(_, c2))  if c2.isLetter                => acc + c2.toUpper
    case (acc, Seq(_, c2))                                => acc + c2
    // Last element of string
    case (acc, Seq(c)) => acc + "?!"
  }

def capitalizeAndInterruptAndQuestionIncredulously(s: String) =
  slidingFoldLeft(s.toSeq, 3)("" + s(0).toUpper) {
    // Normal iteration
    case (acc, Seq(c1, c2, _)) if c1.isLetter && c2.isLetter => acc + c2.toLower
    case (acc, Seq(_, c2, _))  if c2.isLetter                => acc + c2.toUpper
    case (acc, Seq(_, c2, _))                                => acc + c2
    // Last two elements of string
    case (acc, Seq(c1, c2)) => acc + " (commercial break) " + c2
    // Last element of string
    case (acc, Seq(c)) => acc + "?!"
  }

println(capitalizeAndQuestionIncredulously("hello my name is mAtthew"))
println(capitalizeAndInterruptAndQuestionIncredulously("hello my name is mAtthew"))
Hello My Name Is Matthew?!
Hello My Name Is Matthe (commercial break) w?!