Excel+VBA制作数独(二):课堂模块

 2024-03-09 02:06:16  阅读 0

数独上周进行了优化。 在我的笔记本电脑上随机生成 100 个数独的时间在 0.9 秒到 1 秒之间,比以前快了大约 30 倍。 文章里写了优化。

上次介绍完基本方法后,这次就讲解一下类模块的内容。 首先,为什么要使用类模块? 这个问题实际上问的是为什么我们要使用面向对象编程。 我个人不太喜欢在 VBA 中使用类模块。 一是不如其他语言的面向对象功能强大,二是调试困难。 但有时我们不得不使用这种编程方法来简化我们的代码,同时提高可读性、可扩展性等。

这是我在模块中使用的代码:

Public Sub get_sudoku()
Sheet1.Range("A1:I9").ClearContents
Dim s As New Sudoku
Call s.creat_Sudoku
Set s = Nothing
End Sub
Public Sub solute_sudoku()
Dim s As New Sudoku
Call s.creat_Sudoku
Set s = Nothing
End Sub

从方法名称可以看出,第一种方法是随机生成数独,第二种方法是解数独。 他们的核心代码只有三行。 其实去掉最后一行是没有问题的。 然而,数独程序并不能用两行代码轻松完成。

让我们解释一下这几行代码在说什么。 首先,Dim s As New 将 s 声明为一个新类。 类名就像将变量声明为字符串类型一样。 我们称 s 为该类的一个实例。 然后,调用 s。 调用类型中命名的方法,创建数独。

在实际编程中,我们首先创建这个类。

1. 创建数独类

在VBA项目窗口中,右键-插入-类模块即可轻松创建新类。 这时,我们最好先通过属性窗口对类进行重命名。 如果我们让它为默认名称Class 1,我们就只能通过Dims As New Class 1来实例化它。虽然VBA支持中文类名、方法和变量,但用中文编程还是感觉怪怪的。 当然,有时候也利用了中文编程的优势,比如三国志的EXCEL版。 作者几乎全部用中文来命名各种事件和人物。 否则,使用翻译版的南蛮入侵或者青之剑,就会害人害己。

类模块的外观与普通模块没有什么区别,但是如果你仔细观察,你会发现代码窗口上方的两个下拉菜单多了一些额外的内容。 当您选择左侧的班级时,右侧会有两个选项。 点击后,会分别创建两个方法 和 。 这两个是系统默认的方法。 它们不能重命名,也不能添加变量,也不能使用Call指令调用,因为它们分别是在类实例化和释放时自动触发的。 对应上面三行语句,在第一行和第三行触发。

vba定义类型_vba用户定义类型未定义_vb用户定义类型未定义怎么弄

注意:由于VBA类实例化方法不能传入参数,所以如果有需要,我们一般会自己写一个Init方法,实例化后调用,只要不与系统方法同名即可。

创建完类后,我们首先给类添加公共变量,这些变量就是这个类的属性。 类的属性实际上是一个变量。 只要类没有发布,它就会存在,并且可以随意更改和读取。 我不想将封装的概念引入到VBA的类模块中,因为这只会使代码变得更加复杂和难以理解,尽管它看起来更专业。

那么 () 类需要哪些属性呢? 我们可以写出所有我们能想到的全局变量。 如果我们无法全部想到,如有必要,我们可以稍后添加。 我能想到的有以下几种: 1. 一个计数器,记录生成数独的数量; 2. 一个堆栈,用于存储生成的数字,可以随时追溯; 3. 一个数组,用于存储每个单元格的实时值。 地位; 4、三个条件数组,用于存储每一行​​、每一列、每个九方格的约束条件。

注意:在VBA中一般使用集合来实现栈,因为ReDim总是不友好,因为数组的大小是未知的。 但这里的数字大小是固定的,所以我们直接使用数组。

代码如下所示:

Option Explicit
'强制显式变量声明
Public sum As Integer
Public num_arr As Variant
Public cell_arr As Variant
Public row_list As Variant
Public col_list As Variant
Public box_list As Variant

模块中最上面的一个是必须的,因为变量和属性较多,一旦重名很容易混淆。

我在这里将数组定义为不定类型,因为常量、定长字符串、数组、用户定义类型和语句不允许作为对象模块的公共成员。 定义数组的语句会导致编译错误,但我们只能将其定义为不定类型,然后在类方法中重新定义。

所有的类属性都可以通过Me.sum = 0之类的方法进行赋值。即使重新定义数组,也不能通过这种方式对数组中的元素进行赋值。 只能通过以下方式通过临时数组来赋值。

vba定义类型_vba用户定义类型未定义_vb用户定义类型未定义怎么弄

Dim temp_arr As Variant
temp_arr = Me.num_arr
temp_(1, 1) = 0
Set Me.num_arr = temp_arr

对于这么复杂的过程,即使我们把它写成类方法,也不是很友好,而这个方法几乎可以认为是一个封装过程。 其实我们会用一个很巧妙的方法来解决这个尴尬,那就是在属性中给数组赋值的时候,不要包含Me。 如果你用惯了这个部分,肯定会感觉很不舒服。 在其他语言中。 然而,这是一个不起眼的 VBA 类模块,因此强制显式声明变量对于避免这种混乱非常有用。

2. 创建单元类

上面提到的数组需要用来存储每个单元格的实时状态,因为单元格的状态包含很多方面:基本的行、列、九方格索引信息,单元格的初始状态,以及无法存储的数字。 列表。

基于以上考虑,我们插入另一个类模块,命名为Cell,并声明以下属性:

Option Explicit
Public row As Integer
Public col As Integer
Public box As Integer
Public sum As Integer
Public status As Integer
Public value As Integer
Public unable_num_list As Variant

一般来说,对于无法存储的数字列表,从存储空间的角度,我们会使用可变大小的集合或字典。 当在单元格中填写数字时,我们需要确认该数字是否存在于列表中。 从速度的角度考虑,我们会使用字典的方法。 然而,这正是我第一次编程时速度如此缓慢的原因。

因为字典是 VBA 中的对象,所以访问对象比访问数组慢得多。 这也是为什么我一般都会将sheet中的范围赋值给一个arr然后进行操作,因为无论是单元格还是单元格,都比较方便。 对于复杂的对象,访问速度更令人印象深刻。

在数独特殊的数据环境下,数组大小固定为9,存储空间的问题不是问题。 存在的遍历最多只有9次循环,比方法慢不了多少。 所以这两个缺点哪个更好,我最终把代码中的字典全部改成了数组。 数据类型选择参考如下:

1.当查找大量数据时,字典比数组更快。 2.当读写大量数据时,数组比任何对象都快。

3、一维数组的读写速度优于高维数组

以上只是一个参考,没必要过度优化。 毕竟,这不是编程比赛。 有时候一点点的差异并没有太大的影响,只要程序运行时没有明显的滞后即可。 在这个数独实现中,为了直观起见,没有将二维数组简化为一维处理。

3. 创建类方法

cell类的方法比较简单,基本就是使用时赋值。 我们可以给默认初始化方法无法存储的数值数组赋值:

Private Sub Class_Initialize()
ReDim unable_num_list(1 To 9) As Integer
Dim i
%
For i = 1 To 9
  unable_num_list(i) = 1
Next i
End Sub

VBA中布尔变量True对应的整数为-1,False对应的整数为0,这里用1和0表示。

对于其他属性的赋值,我们需要自己写一个方法:

Public Sub setValue(ByVal r As Integer, ByVal c As Integer, ByVal b As Integer, ByVal v As Integer, Optional ByVal s As Integer = 0)
Me.row = r
Me.col = c
Me.box = b
Me.sum = (r - 1) * 9 + c
Me.value = v
Me.status = s
unable_num_list(v) = 0
End Sub

这里分别传入行数、列数、网格数,然后根据行数和列数计算出总数(注:这是二维降为一维的方法),以及传入值和单元格的初始状态。 最后,将无法存储的数字数组中该位置的值标记为 0。

Cell类已经基本创建完毕。 我也对一个类的基本创建和方法构建有了一个大概的了解。 下次我会继续完成课程。

标签: 数组 变量 存放

如本站内容信息有侵犯到您的权益请联系我们删除,谢谢!!


Copyright © 2020 All Rights Reserved 京ICP5741267-1号 统计代码