ソート関数を書くときにいろいろと機能を追加したくなるが
ベースの関数を再利用する形で拡張する方法を調べてみた
試したことは以下
- 引数を増やしていくパターン(失敗)
- 引数を分割していくパターン(成功?)
前提条件
以下の関数への適用を考える
const array = make_data(100)
array.sort(sort_function)
引数を増やしていくパターン(失敗)
ソート関数1
基本のソート関数
import { describe, it } from 'vitest'
function make_data(N: number) {
const array = new Array(N)
for (let i = 0; i < N; ++i) array[i] = Math.floor(Math.random() * N)
return array
}
function test_sort_basic(a: number, b: number) {
return a - b
}
describe('引数を増やしていくパターン(失敗)', () => {
it('1-基本のソート関数', () => {
const array = make_data(100)
console.log(array)
const sort_function = test_sort_basic
array.sort(sort_function)
console.log(array)
})
})
実行結果
[
4, 51, 28, 62, 10, 61, 79, 53, 24, 27, 86, 6,
39, 0, 93, 48, 71, 1, 58, 24, 63, 3, 24, 6,
52, 80, 34, 86, 9, 42, 14, 13, 61, 96, 0, 53,
92, 33, 57, 35, 90, 13, 73, 20, 96, 2, 18, 92,
20, 29, 43, 91, 51, 38, 41, 96, 45, 91, 26, 60,
90, 16, 78, 68, 34, 2, 26, 12, 53, 13, 83, 19,
45, 15, 89, 64, 18, 78, 63, 96, 92, 13, 98, 98,
34, 69, 27, 60, 26, 21, 95, 19, 73, 9, 3, 7,
46, 67, 93, 5
]
[
0, 0, 1, 2, 2, 3, 3, 4, 5, 6, 6, 7,
9, 9, 10, 12, 13, 13, 13, 13, 14, 15, 16, 18,
18, 19, 19, 20, 20, 21, 24, 24, 24, 26, 26, 26,
27, 27, 28, 29, 33, 34, 34, 34, 35, 38, 39, 41,
42, 43, 45, 45, 46, 48, 51, 51, 52, 53, 53, 53,
57, 58, 60, 60, 61, 61, 62, 63, 63, 64, 67, 68,
69, 71, 73, 73, 78, 78, 79, 80, 83, 86, 86, 89,
90, 90, 91, 91, 92, 92, 92, 93, 93, 95, 96, 96,
96, 96, 98, 98
]
ソート関数2
uncurry_sort_basic_2のような変換を毎回作成する必要があり、失敗したような気がする
昇順、降順を追加したソート関数
import { describe, it } from 'vitest'
function make_data(N: number) {
const array = new Array(N)
for (let i = 0; i < N; ++i) array[i] = Math.floor(Math.random() * N)
return array
}
type type_sort_basic_2 = (asc: boolean, a: number, b: number) => number
const uncurry_sort_basic_2 = (f: type_sort_basic_2) => (asc: boolean) => (a: number, b: number) =>
f(asc, a, b)
function test_sort_basic(a: number, b: number) {
return a - b
}
function test_sort_basic_2(asc: boolean, a: number, b: number) {
if (asc) return test_sort_basic(a, b)
else return test_sort_basic(b, a)
}
describe('引数を増やしていくパターン(失敗)', () => {
it('2-昇順、降順つきのソート関数', () => {
const array = make_data(100)
console.log(array)
const sort_function = uncurry_sort_basic_2(test_sort_basic_2)(false)
array.sort(sort_function)
console.log(array)
})
})
実行結果
[
37, 3, 63, 50, 75, 24, 43, 4, 38, 88, 57, 56,
20, 72, 38, 22, 15, 12, 39, 14, 64, 97, 73, 3,
54, 39, 78, 74, 65, 28, 74, 51, 12, 60, 26, 80,
89, 99, 56, 56, 66, 89, 74, 92, 45, 20, 62, 94,
98, 52, 56, 0, 12, 6, 65, 5, 23, 45, 99, 93,
22, 19, 80, 31, 28, 84, 58, 92, 54, 53, 64, 28,
25, 77, 14, 53, 37, 63, 41, 22, 86, 84, 12, 25,
60, 0, 12, 60, 7, 84, 43, 50, 4, 7, 10, 77,
35, 68, 88, 44
]
[
99, 99, 98, 97, 94, 93, 92, 92, 89, 89, 88, 88,
86, 84, 84, 84, 80, 80, 78, 77, 77, 75, 74, 74,
74, 73, 72, 68, 66, 65, 65, 64, 64, 63, 63, 62,
60, 60, 60, 58, 57, 56, 56, 56, 56, 54, 54, 53,
53, 52, 51, 50, 50, 45, 45, 44, 43, 43, 41, 39,
39, 38, 38, 37, 37, 35, 31, 28, 28, 28, 26, 25,
25, 24, 23, 22, 22, 22, 20, 20, 19, 15, 14, 14,
12, 12, 12, 12, 12, 10, 7, 7, 6, 5, 4, 4,
3, 3, 0, 0
]
引数を分割していくパターン(成功?)
ソート関数2
昇順、降順を追加したソート関数
import { describe, it } from 'vitest'
function make_data(N: number) {
const array = new Array(N)
for (let i = 0; i < N; ++i) array[i] = Math.floor(Math.random() * N)
return array
}
function test_sort_basic(a: number, b: number) {
return a - b
}
const test_sort_hof_2 = (asc: boolean) => (a: number, b: number) => {
if (asc) return test_sort_basic(a, b)
else return test_sort_basic(b, a)
}
describe('引数を分割していくパターン(成功?)', () => {
it('2-昇順、降順つきのソート関数', () => {
const array = make_data(100)
console.log(array)
const sort_function = test_sort_hof_2(false)
array.sort(sort_function)
console.log(array)
})
})
実行結果
[
88, 53, 15, 61, 75, 43, 0, 34, 63, 42, 60, 58,
31, 89, 84, 76, 8, 60, 96, 33, 46, 47, 88, 71,
40, 88, 14, 91, 88, 42, 85, 66, 70, 15, 35, 73,
0, 59, 55, 13, 70, 9, 69, 19, 7, 93, 96, 73,
27, 55, 36, 14, 77, 8, 20, 9, 54, 97, 74, 71,
79, 17, 69, 49, 72, 88, 94, 72, 71, 41, 27, 23,
35, 75, 76, 60, 90, 37, 19, 54, 12, 25, 30, 41,
55, 74, 68, 9, 36, 25, 75, 57, 93, 39, 14, 14,
73, 42, 49, 59
]
[
97, 96, 96, 94, 93, 93, 91, 90, 89, 88, 88, 88,
88, 88, 85, 84, 79, 77, 76, 76, 75, 75, 75, 74,
74, 73, 73, 73, 72, 72, 71, 71, 71, 70, 70, 69,
69, 68, 66, 63, 61, 60, 60, 60, 59, 59, 58, 57,
55, 55, 55, 54, 54, 53, 49, 49, 47, 46, 43, 42,
42, 42, 41, 41, 40, 39, 37, 36, 36, 35, 35, 34,
33, 31, 30, 27, 27, 25, 25, 23, 20, 19, 19, 17,
15, 15, 14, 14, 14, 14, 13, 12, 9, 9, 9, 8,
8, 7, 0, 0
]
ソート関数3
昇順、降順と特定の値を先頭か末尾に持っていくを追加したソート関数
import { describe, it } from 'vitest'
function make_data(N: number) {
const array = new Array(N)
for (let i = 0; i < N; ++i) array[i] = Math.floor(Math.random() * N)
return array
}
function test_sort_basic(a: number, b: number) {
return a - b
}
const test_sort_hof_2 = (asc: boolean) => (a: number, b: number) => {
if (asc) return test_sort_basic(a, b)
else return test_sort_basic(b, a)
}
const test_sort_hof_3 = (pick: number[] | number) => (asc: boolean) => (a: number, b: number) => {
if (Array.isArray(pick)) {
const is_pick_a = pick.includes(a)
const is_pick_b = pick.includes(b)
if (is_pick_a && is_pick_b && a !== b) return test_sort_hof_2(asc)(a, b)
else if (is_pick_a) return asc ? -1 : 1
else if (is_pick_b) return asc ? 1 : -1
else return test_sort_hof_2(asc)(a, b)
} else {
if (a == pick) return asc ? -1 : 1
else if (b == pick) return asc ? 1 : -1
else return test_sort_hof_2(asc)(a, b)
}
}
describe('引数を分割していくパターン(成功?)', () => {
it('昇順、降順と特定の値1つを先頭か末尾に持っていくソート関数', () => {
const array = make_data(100)
console.log(array)
const sort_function = test_sort_hof_3(7)(true)
array.sort(sort_function)
console.log(array)
})
it('昇順、降順と特定の値複数を先頭か末尾に持っていくソート関数', () => {
const array = make_data(100)
console.log(array)
const sort_function = test_sort_hof_3([1, 11, 7, 77])(false)
array.sort(sort_function)
console.log(array)
})
})
実行結果
特定の値が1つの場合
[
38, 90, 70, 23, 79, 27, 49, 43, 0, 89, 18, 30,
80, 50, 23, 58, 92, 12, 15, 8, 84, 97, 81, 33,
63, 30, 27, 18, 50, 86, 78, 89, 81, 38, 56, 76,
76, 94, 85, 21, 71, 56, 25, 10, 93, 64, 47, 67,
85, 18, 5, 68, 74, 63, 23, 86, 31, 69, 81, 94,
59, 11, 89, 68, 99, 65, 29, 72, 18, 96, 7, 63,
13, 38, 50, 25, 45, 33, 96, 3, 65, 49, 91, 42,
75, 25, 59, 94, 87, 78, 2, 13, 86, 47, 98, 81,
98, 63, 99, 25
]
[
7, 0, 2, 3, 5, 8, 10, 11, 12, 13, 13, 15,
18, 18, 18, 18, 21, 23, 23, 23, 25, 25, 25, 25,
27, 27, 29, 30, 30, 31, 33, 33, 38, 38, 38, 42,
43, 45, 47, 47, 49, 49, 50, 50, 50, 56, 56, 58,
59, 59, 63, 63, 63, 63, 64, 65, 65, 67, 68, 68,
69, 70, 71, 72, 74, 75, 76, 76, 78, 78, 79, 80,
81, 81, 81, 81, 84, 85, 85, 86, 86, 86, 87, 89,
89, 89, 90, 91, 92, 93, 94, 94, 94, 96, 96, 97,
98, 98, 99, 99
]
実行結果
特定の値が複数の場合
[
58, 6, 71, 57, 69, 45, 57, 68, 49, 69, 83, 15,
68, 51, 76, 39, 12, 79, 20, 59, 67, 9, 56, 88,
61, 62, 6, 67, 51, 39, 11, 49, 48, 13, 45, 99,
52, 89, 93, 43, 88, 27, 34, 59, 66, 34, 91, 7,
47, 71, 40, 62, 82, 37, 63, 58, 90, 11, 35, 48,
91, 79, 54, 55, 76, 45, 43, 32, 72, 3, 41, 83,
16, 67, 50, 31, 28, 1, 96, 53, 7, 51, 37, 72,
67, 53, 16, 26, 73, 88, 44, 39, 84, 47, 90, 83,
77, 37, 3, 83
]
[
99, 96, 93, 91, 91, 90, 90, 89, 88, 88, 88, 84,
83, 83, 83, 83, 82, 79, 79, 76, 76, 73, 72, 72,
71, 71, 69, 69, 68, 68, 67, 67, 67, 67, 66, 63,
62, 62, 61, 59, 59, 58, 58, 57, 57, 56, 55, 54,
53, 53, 52, 51, 51, 51, 50, 49, 49, 48, 48, 47,
47, 45, 45, 45, 44, 43, 43, 41, 40, 39, 39, 39,
37, 37, 37, 35, 34, 34, 32, 31, 28, 27, 26, 20,
16, 16, 15, 13, 12, 9, 6, 6, 3, 3, 77, 11,
11, 7, 7, 1
]
まとめ
基本関数に対して引数を増やすような拡張をする場合は
関数を返すような形で引数を追加していったほうがよさそう?