3.3.4.2用 F# 求列表中数字的和

本文介绍了如何使用F#语言编写递归函数sumList对整数列表进行求和,并通过F#Interactive进行了测试验证。

 

3.3.4.2用 F# 求列表中数字的和

 

我们已经知道了用 C# 实现的代码,现在,再尝试用 F# 实现同样的功能。清单 3.16 是F# 函数 sumList,并用几个F# Interactive 命令进行了测试。

 

清单 3.16对列表中元素求和(F# Interactive)

> let rec sumList list =

   match list with  <- 根据模式,匹配列表

   | []-> 0    [1]

   |head::tail -> head + sumList(tail) [2]

;;

val sumList : int list -> int  [3]

 

> let list = [ 1 .. 5 ];;  <- 创建测试列表

val list : int list

 

> sumList(list);;  <- 计算,输出和

val it : int = 15

 

如果和前面用 C# 实现的代码相比,会发现有很多相似之处。与前面的情况一样,有两个分支,一个用于为空列表[1],一个用于 cons cell [2],第二个分支也使用递归实现。显著的区别在于,F# 可以使用模式匹配选择执行路径。模式匹配还能从 cons cell 中提取值,因此,执行一旦进入第二个分支, head 和 tail 的值就有了。因此,不会使用到没有被模式匹配的值,能增加了代码的稳健性。这听起来微不足道,但能防止代码意外地试图访问(不存在)空列表的元素。模式匹配是函数语言天生的结构,C# 中没有对应的功能,所以,只能使用条件运算符(?:)来实现相同的行为。

另外,F# 类型推断还有帮助:不必在代码中显式指定类型。可以看到,它正确推断出函数取一个整数列表,返回整数[3]。推理算法测试 list 值是空列表,或cons cell,推断出值的类型是列表。从一个分支将返回 0,可知整个函数返回整数;因为,我们是把列表中的元素加到一起,可以推断出参数是包含整数的列表。

这一节中的递归概念非常重要,但是,写递归很难。在下一节,我们将介绍一种机制,能够隐藏代码中困难的递归部分。

 

----------

 

[4] 我们可以在代码中添加对 foreach 语句的支持,对于FuncList 类型,这样做是值得的。

import io import os.path import tkinter as tk from os import listdir from tkinter import messagebox import os import PIL import numpy as np from PIL import Image import cv2 import joblib class digitCanvas: # # 1.入口文件,设置各种参数 def __init__(self,title="数字画板",rootW=300): self.title=title self.root=tk.Tk() self.rootW=rootW self.rootH=400 self.rootX=300 self.canvasW=self.rootW self.canvasH=int(self.rootH/2) self.rootY=300 self.textWidth=20 self.isLoadModel=False self.knn=None self.__ui() # 2.设置软件的界面 def __ui(self): self.root.title(self.title) self.root.geometry(f"{self.rootW}x{self.rootH}+{self.rootX}+{self.rootY}") # 设置画板 canvas=tk.Canvas(self.root,width=self.canvasW,height=self.canvasH,bg="#fff") canvas.grid(row=0,column=0,columnspan=5) self.lastx=None self.lasty=None canvas.bind("<B1-Motion>",self.__draw) canvas.bind("<ButtonRelease-1>",self.__release) self.canvas=canvas # 设置输入框的文字 frame=tk.Frame(self.root) frame.grid(row=1,column=0,columnspan=5,pady=10) # 设置提示文字 text=tk.Label(frame,text="请输入(0-9)的数字:") text.pack(side="left") # 设置输入框 input = tk.Entry(frame) input.pack(side="left") self.input=input # 设置清空按钮按钮 clearBtn=tk.Button(self.root,text="清空画布",command=self.__clearFun) clearBtn.grid(row=2,column=0,pady=10) self.clearBtn = clearBtn # 设置保存按钮按钮 saveBtn = tk.Button(self.root, text="保存",command=self.__saveFun) saveBtn.grid(row=2, column=1, pady=10) self.saveBtn = saveBtn # 设置训练按钮按钮 trainBtn = tk.Button(self.root, text="开始训练",command=self.__train) trainBtn.grid(row=2, column=2, pady=10) self.trainBtn = trainBtn # 设置预测按钮按钮 preBtn = tk.Button(self.root, text="预测",command=self.__preFun) preBtn.grid(row=2, column=3, pady=10) self.preBtn = preBtn # 设置载入按钮按钮 loadBtn = tk.Button(self.root, text="载入模型",command=self.__loadModel) loadBtn.grid(row=2, column=4, pady=10) self.loadBtn = loadBtn # 提示消息 notice = tk.Label(self.root, text="请开始操作",fg="red") notice.place(x=200,y=350) self.notice=notice self.root.mainloop() # 3. 写字 # 3.1 开始写字 def __draw(self, e): if self.lastx and self.lasty: self.canvas.create_line(self.lastx, self.lasty, e.x, e.y, fill="black", width=self.textWidth, capstyle="round") self.lastx = e.x self.lasty = e.y # 3.2.写字完毕 def __release(self, e): self.lastx = None self.lasty = None # 3.3 清空画布 def __clearFun(self): self.canvas.delete("all") self.notice.config(text="画布已清空") # 4. 保存内容 def __saveFun(self): label = self.input.get() if not label.isdigit() or int(label) > 9 or int(label) < 0: messagebox.showerror(message="请输入0-9之间的数字") return dirname = f"images/{label}" if not os.path.exists(dirname): os.makedirs(dirname) filename = f"{label}_{len(os.listdir(dirname)) + 1}.png" fullname = dirname + "/" + filename # 调用该方法,将画布中的图形保存成指定的文件 self.__canvas_save_png(fullname) self.notice.config(text="图像已保存") # 4.1. 将画布保存成png def __canvas_save_png(self, fullname): img = self.canvas.postscript(colormode="color") img = Image.open(io.BytesIO(img.encode("utf-8"))) img = img.convert("RGB") img = img.convert("L") tempname = "tempimg.png" img.save(tempname) imgdatas = self.__preprocess_img(tempname) imgdatas = (imgdatas.reshape((28, 28)) * 255).astype(np.uint8) Image.fromarray(imgdatas).save(fullname) if os.path.exists(tempname): os.remove(tempname) # 4.2.预处理 读取图形内容 缩放转换 def __preprocess_img(self, filepath): # 将原始图片的像素读入到内存里面 img = cv2.imread(filepath, cv2.IMREAD_GRAYSCALE) # 获取图像边缘 img = 255 - img find, _ = cv2.findContours(img, 1, 2) cont = max(find, key=cv2.contourArea) x, y, w, h = cv2.boundingRect(cont) short = img[y:y + h, x:x + w] # 缩放 x, y = short.shape scale = 20 / (max(x, y)) resize = cv2.resize(short, None, fx=scale, fy=scale, interpolation=cv2.INTER_AREA) # 将缩放的图片放到28x28的中央 y, x = resize.shape newx = (28 - x) // 2 newy = (28 - y) // 2 newimg = np.zeros((28, 28), dtype=np.uint8) newimg[newy:newy + y, newx:newx + x] = resize newimg = newimg / 255 return newimg.flatten() # 5. 加载数据,训练数据,评估数据,优化参数,保存模型 def __train(self): self.notice.config(text="开始训练...") # 1. 加载数据数据 datas, target = self.__loadDatas() # 2. 开始训练 # knn # 2.1 导入相应的库,算法、评估模型(写你的代码) # 2.2 拆分数据集(写你的代码) # 2.3 进行评估,从最好的,最好的距离算法,是否加入权重 best_score = 0 best_k = 2 best_p = 1 best_w = "" #(写你的代码) print(f"最高分是({best_score}):k={best_k},w={best_w},p={best_p}") # 2.4 通过最好的参数,创建模型(写你的代码) # 2.5 将拟合的模型,进行保存(写你的代码) self.notice.config(text="训练完成,模型已生成,要预测请载入模型") # 5.1加载数据 def __loadDatas(self): rootdir = "images" datas = [] target = [] for dirname in os.listdir(rootdir): for filename in os.listdir(rootdir + "/" + dirname): fullname = rootdir + "/" + dirname + "/" + filename data = self.__preprocess_img(fullname) datas.append(data) target.append(int(dirname)) return np.array(datas), np.array(target) # 6.载入模型 def __loadModel(self): if self.isLoadModel: self.notice.config(text="模型已经载入了") return self.isLoadModel = True self.knn = joblib.load("knn_model.pkl") # 7. 预测函数 def __preFun(self): #1.先载入训练好的模型 if not self.isLoadModel: messagebox.showerror(message="请先载入模型") return # 2.将预测的数字转换成标准的格式 self.__canvas_save_png("pre.png") # 3. 将转换完的图片,转换成矩阵,用于运算 predata=self.__preprocess_img("pre.png").reshape(1,-1) # 4. 将图片数据用KNN算法进行预测 result=self.knn.predict(predata) # 5. 将预测的结果输出 self.notice.config(text=f"预测的结果是{result}") obj=digitCanvas(rootW=500)补充代码缺失部分,使其完成手写数字识别的功能
最新发布
09-23
以下是一个完整的手写数字识别 Python 代码示例,包含导入库、拆分数据集、评估参数、创建模型保存模型等步骤。这里使用 PyTorch 框架,以 MNIST 数据集为例: ```python import torch import torch.nn as nn import torch.optim as optim from torchvision import datasets, transforms from torch.utils.data import DataLoader import torch.nn.functional as F # 数据预处理 transform = transforms.Compose([ transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,)) ]) # 加载数据集 train_dataset = datasets.MNIST(root=&#39;./data&#39;, train=True, download=True, transform=transform) test_dataset = datasets.MNIST(root=&#39;./data&#39;, train=False, download=True, transform=transform) # 拆分数据集 train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True) test_loader = DataLoader(test_dataset, batch_size=64, shuffle=False) # 创建模型 class Net(nn.Module): def __init__(self): super(Net, self).__init__() self.conv1 = nn.Conv2d(1, 10, kernel_size=5) self.conv2 = nn.Conv2d(10, 20, kernel_size=5) self.fc1 = nn.Linear(320, 50) self.fc2 = nn.Linear(50, 10) def forward(self, x): x = F.relu(F.max_pool2d(self.conv1(x), 2)) x = F.relu(F.max_pool2d(self.conv2(x), 2)) x = x.view(-1, 320) x = F.relu(self.fc1(x)) x = self.fc2(x) return F.log_softmax(x, dim=1) model = Net() # 定义损失函数优化器 criterion = nn.NLLLoss() optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.5) # 训练模型 def train(model, train_loader, optimizer, epoch): model.train() for batch_idx, (data, target) in enumerate(train_loader): optimizer.zero_grad() output = model(data) loss = criterion(output, target) loss.backward() optimizer.step() if batch_idx % 100 == 0: print(&#39;Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}&#39;.format( epoch, batch_idx * len(data), len(train_loader.dataset), 100. * batch_idx / len(train_loader), loss.item())) # 评估模型 def test(model, test_loader): model.eval() test_loss = 0 correct = 0 with torch.no_grad(): for data, target in test_loader: output = model(data) test_loss += criterion(output, target).item() pred = output.argmax(dim=1, keepdim=True) correct += pred.eq(target.view_as(pred)).sum().item() test_loss /= len(test_loader.dataset) print(&#39;\nTest set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n&#39;.format( test_loss, correct, len(test_loader.dataset), 100. * correct / len(test_loader.dataset))) # 训练评估 epochs = 5 for epoch in range(1, epochs + 1): train(model, train_loader, optimizer, epoch) test(model, test_loader) # 保存模型 torch.save(model.state_dict(), &#39;mnist_model.pth&#39;) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值