阅读视图

发现新文章,点击刷新页面。
🔲 ☆

FoldLeft and FoldRight

看 [[Scala 函数式编程]] 当中做的练习,还挺有意思。

实现了 foldLeft 合 foldRight 之后,如何使用 foldRight / foldLeft 实现 foldRight / foldLeft?

Hard: Can you write foldRight in terms of foldLeft? How about the other way around? Implementing foldRight via foldLeft is useful because it lets us implement foldRight tail recursively, which means it works even for large lists without overflowing the stack.

1
2
3
4
5
6
7
def foldRight[A,B](as: List[A], z: B)(f: (A, B) => B): B = as match
 case Nil => z
 case x::xs => f(x, foldRight(xs, z)(f))

def foldLeft[A,B](as: List[A], z:B)(f: (B, A) => B): B = as match
 case Nil => z
 case x::xs => foldLeft(xs, f(z, x))(f)

看到这个第一反应想到一个很 trick 的想法: foldRight 其实不就等于 reverse 之后再 foldLeft 吗?刚好前面也用 foldLeft 实现了 reverse.

1
2
3
4
def reverse[A](list: List[A]): List[A] = foldLeft(list, List.empty[A])((acc, h) => h::acc)

def foldRightViaFoldLeft[A, B](l: List[A], acc: B, f: (A, B) => B): B
 = foldLeft(reverse(l), acc)(f)

除了这种 trick 以外,还有没其他方法呢?搜了一下官方解答,给的另一种办法是这样的

1
2
def foldRightViaFoldLeft[A, B](l: List[A], acc: B, f: (A, B) => B): B =
 foldLeft(l, (b: B) => b)((g, a) => (b: B) => g(f(a, b)))(acc)

看了好一会,然后删掉自己重新一次之后,才理解过来。思路就是在 foldLeft 的过程,维护一个 acc 的函数,然后将前面的元素逐个塞到这个函数里面,越后面的元素塞在越里层,最右面的函数最先开始处理。通过 foldLeft 得到这个函数后,再传初值调用即可。

让 gpt 生成的一个例子

  • 假设我们有一个列表 List(1, 2, 3),一个初始值 0,和一个函数 f = (a: Int, b: Int) => a + b。调用 foldRightViaFoldLeft_1(List(1, 2, 3), 0, (a, b) => a + b) 会构建如下的函数链:
    • 初始值是 (b: Int) => b
    • 第一个元素 1(g, a) => b => g(f(a, b)) 变成 (b: Int) => (b: Int) => b + 1
    • 第二个元素 2(b: Int) => ((b: Int) => b + 1)(b + 2)
    • 第三个元素 3(b: Int) => (((b: Int) => b + 1)(b + 2))(b + 3)
  • 最终这个链会被应用到初始值 0 上,得到 0 + 3 + 2 + 1,即 6

说实话有了思路,写起来也还是挺绕的,但是写着写着意识到,其实可以根据类型来判断填什么值,这样就很好写了。

🔲 ⭐

LeetCode: Make a Square with the Same Color

Problem

source: 3127. Make a Square with the Same Color

difficulty: easy

You are given a 2D matrix grid of size 3 x 3 consisting only of characters 'B' and 'W'. Character 'W' represents the white color, and character 'B' represents the black color.

Your task is to change the color of at most one cell so that the matrix has a 2 x 2 square where all cells are of the same color.

Return true if it is possible to create a 2 x 2 square of the same color, otherwise, return false.

Constraints:

  • grid.length == 3
  • ``grid[i].length == 3`
  • grid[i][j] is either 'W' or 'B'.

Solution

问题可以转化成判断 matrix 是否存在一个 2 * 2 的 matrix,其中包含 3 or 4 个相同的元素。

但是再仔细一看 matrix 只有 3 * 3,因此可以直接分成 4 个 block,分别判断 4 个 block 是否存在就行了。

Kotlin

直接判断每一个点分别在哪些 block 里面,但是这几个 if 判断就写得很丑陋(写了一个之后干脆打开 GitHub Copilot 生成算了)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
class Solution {
 fun canMakeSquare(grid: Array<CharArray>): Boolean {
 val cnt = IntArray(4)
 fun Char.value(): Int = if (this == 'B') 1 else -1

 for (i in 0..2) {
 for (j in 0..2) {
 if ( i < 2 && j < 2) cnt[0] += grid[i][j].value()
 if ( i < 2 && j > 0) cnt[1] += grid[i][j].value()
 if ( i > 0 && j < 2) cnt[2] += grid[i][j].value()
 if ( i > 0 && j > 0) cnt[3] += grid[i][j].value()
 }
 }

 return cnt.any() { it in listOf(2, -2, 4, -4) }
 }
}

Scala

最近在学 Scala,于是自然又用 Scala 写一次。相比于此前丑陋的 if 判断,干脆写得更加函数式,试一下 loop free。

直接三行搞定。

1
2
3
4
5
6
7
8
9
object Solution {
 def canMakeSquare(grid: Array[Array[Char]]): Boolean = {
 def getRange = for { i <- 0 to 1; j <- 0 to 1} yield (i, j)

 def check(x: Int, y: Int): Boolean = getRange.count((i, j) => grid(x + i)(y + j) == 'B') != 2

 getRange.count((i, j) => check(i, j)) >= 1
 }
}
❌