IT技术熟练度 v1.0

为衡量个人能力水平自创的一套评分机制,根据时间、代码行数、基础理论三个变量生成。最近在学lua,正好练下基本功。效果可见 个人介绍 | 代码统计 - 小能日记 - 博客园 (cnblogs.com)

life.lua 记录自己每日的IT活动,main.lua 程序根据life.lua生成文件 output.md

具体规则

  • 某一条目为A对象实例(如Lua),初始化40分,范围 (0,100]

    • [0,20) 遗忘、[20,40) 生疏、[40,60) 了解、[60,80) 熟悉、[80,100] 熟练
  • 时间:每隔1天,进行如下模拟遗忘操作,从熟练到遗忘需要90天不敲代码

    • 小于等于80分的条目减1分
    • 小于等于100分的条目减2分
  • 代码行数:根据每天敲的代码有效行数(不含空行)对熟练度增减。

    • 动态计算每个条目平均有效行AVG(4天敲800行得200),不足4天默认按500行计算
    • 当天有效行数处于 [AVG * 1.25,∞] ,加4分
    • 当天有效行数处于 [AVG * 0.75,AVG * 1.25] ,加3分
    • 当天有效行数处于 [AVG * 0.25,AVG * 0.75) ,加2分
    • 当天有效行数处于 [AVG * 0.10,AVG * 0.25) ,加1分
    • 当天有效行数处于 [AVG * 0.00,AVG * 0.10) ,加0分
    • 从50分到80分需要连续敲15天,从80分到100分需要连续敲20天。
    • 越熟练代码越精简,行数逐渐变少,这是量变到质变的过程,故 AVG * 0.75
  • 基础理论:当天涉及基础理论(如网络、算法、设计模式、架构技巧等)

    • 集合元素个数 * 2,如["算法"]:{"马尔科夫链","迪杰斯特拉"},共2个元素,加4分
  • 状态:状态根据当天敲的所有行数比较 AVG = (一生敲的总行数) // (总天数),不足4天默认按500行计算

    • 加班狂魔:[1.25 * AVG,∞]、正常上班:[AVG * 0.50,AVG * 1.25]、天天摸鱼:[AVG * 10,AVG * 0.50 ]
    • 其他与没敲代码的日子均统计到"摆烂"中。学了理论不敲等于没学

IT活动记录 life.lua

-- 文件 life.lua 格式如下
life = {{
["date"] = "2023年8月16日", -- 日期
["lua"] = {484, 52}, -- 敲的有效行/注释行
["c#"] = {123, 4}, -- 敲的有效行/注释行
["算法"] = {"马尔科夫链", "迪杰斯特拉", "位图法"}, -- 涉及的概念
["设计模式"] = {"单例模式"} -- 涉及的概念
}, {
["date"] = "2023年8月15日",
["lua"] = {477, 18}
}, {
["date"] = "2023年8月14日",
["lua"] = {519, 29}
}, {
["date"] = "2023年8月13日",
["lua"] = {628, 71}
}}

输出熟练度 output.md

(如果有前置head.md则会在这拼接)
Time : 2023/8/13 ~ 2023/8/16 |技术|学习天数|熟练度 (0~100)|程度|评级|
|------|----|----|----|----|
|lua|4|50|了解||
|算法|1|46|了解||
|c#|1|42|了解||
|设计模式|1|42|了解|| |我的一生|总共行数|天数|平均行数
|------|----|----|----|
|lua|2278|4|569|
|c#|127|1|127| |我的状态|天数|
|------|----|
|加班狂魔|1|
|正常上班|3|
|天天摸鱼|0|
|开摆去咯|0|

部分实现

初始化

local scores = {} -- 条目的熟练度,键=条目,值=分数
local levelDays = {0, 0, 0, 0} -- 迄今为止效率,键=状态{摆烂,摸鱼,正常,高强度},值=天数
local studyDays = {} -- 每个条目各自学习了几天
local linesCount = {} -- 每个条目各自敲了几开始行,键=条目,值=行数
local startDay = nil -- 从什么时候开始
local endDay = nil -- 从什么时候结束
local tmpDay = nil -- 当前记录前一个记录的日子

读取记录

主要用了一个有状态的迭代器,配合泛型for循环使用。没有用到不可变状态和控制变量,但是个人习惯所以留着形参s、c。

-- 记录迭代器,逐条返回记录
function getRecords()
local i = #life
return function(s, c)
if i > 0 then
i = i - 1
return life[i + 1]
end
return nil
end, nil, nil
end

时间计算

用模式匹配捕获年月日,并保存最早的时间和最晚的时间。另外计算两个时间之间的间隔sep,根据间隔大小更新scores。

-- 根据每条记录更新 startDay 跟 endDay
-- 计算前一条记录与后一条记录的间隔天数,统计至levelDays并进行scores每日遗忘操作
function updateTime(date)
local y, m, d = string.match(date, "(%d+)年(%d+)月(%d+)")
if not startDay then
startDay = {
year = y,
month = m,
day = d
}
end
tmpDay = tmpDay or startDay
endDay = {
year = y,
month = m,
day = d
}
-- 计算前一条记录与后一条记录的间隔天数 sep
local sep = os.difftime(os.time(endDay), os.time(tmpDay)) // 3600 // 24
-- 间隔大于 1 天,将sep - 1的天数归入摆烂,并进行每日遗忘操作
if sep > 1 then
updateLevelDays(1, sep - 1)
dailyScores(sep)
else
dailyScores(1)
end
tmpDay = endDay
end

遗忘操作

根据规则对scores进行减少

-- 每日遗忘
function dailyScores(n)
for k, v in pairs(scores) do
if v <= 80 then
v = v - 1 * (n or 1)
elseif v <= 100 then
v = v - 2 * (n or 1)
end
scores[k] = v <= 0 and 0 or v
end
end

每条记录的迭代过程

-- 根据每条记录更新 scores levelDays linesCount
function update(record)
local countLines = 0 -- 当天写过的代码行数总和
for item, v in pairs(record) do
-- 判断是编程语言还是基础理论
if type(v[1]) == "number" then
local total = v[1] + (v[2] or 0)
countLines = countLines + total
updateLinesCount(item, total)
updateStudyDays(item)
local level = level(item, total)
updateScores(item, total, level)
elseif type(v[1]) == "string" then
updateScores(item, #v)
updateStudyDays(item)
else
error("条目格式有错", 2)
end
end
-- 更新 levelDays , 保存当天状态 {摆烂,摸鱼,正常,高强度}
updateLevelDaysByCountLines(countLines)
end

主程序

-- 主程序
for r in getRecords() do
-- 根据当前记录时间更新
updateTime(r["date"])
-- 删除"date"减少多余的判断
r["date"] = nil
-- 更新 scores levelDays linesCount
update(r)
end
-- 根据系统时间更新一次时间
updateTime(os.date("%Y年%m月%d日"))

排序

table.sort 只能序列使用,所以每张表先各自拷贝成一份序列再排序

-- 排个序 scores,linesCount
scoresSort = {}
for k, v in pairs(scores) do
scoresSort[#scoresSort + 1] = {k, v}
end
linesCountSort = {}
for k, v in pairs(linesCount) do
linesCountSort[#linesCountSort + 1] = {k, v}
end
table.sort(scoresSort, function(a, b)
return a[2] > b[2] -- 熟练度分数对比
end)
table.sort(linesCount, function(a, b)
return a[2] > b[2] -- 总行数对比
end)

前置拼接

如果前面还需要有固定内容,可以添加个head.md文件。如果存在则自动拼接

io.output(io.open("output.md", "w"))
-- 做前置拼接,一般是个人介绍的内容
pcall(function()
f = io.open("head.md", "r")
if f then
io.write(f:read("a"))
io.write "\n"
f:close()
end
end)

输出表格

输出表格按markdown格式进行输出

-- 输出各个表格
io.write("# IT技术熟练度\n")
io.write(string.format("**Time : %d/%d/%d ~ %d/%d/%d**\n", startDay.year, startDay.month, startDay.day, endDay.year,
endDay.month, endDay.day))
io.write("|技术|学习天数|熟练度 (0~100)|程度|评级|\n")
io.write("|------|----|----|----|----|\n")
local cd = {"遗忘", "生疏", "了解", "熟悉", "熟练"}
for i, v in ipairs(scoresSort) do
local stars = ""
for i = 1, v[2] // 20 + 1, 1 do
stars = stars .. ""
end
io.write(string.format("\z
|%s|%s|%d|%s|%s|\n\z
\z", v[1], studyDays[v[1]], v[2], cd[v[2] // 20 + 1], stars))
end
io.write "\n"

完整代码 main.lua

-- By 小能喵喵喵 2023年8月17日03:28:14
-- https://www.cnblogs.com/linxiaoxu
-- 初始化
local scores = {} -- 条目的熟练度,键=条目,值=分数
local levelDays = {0, 0, 0, 0} -- 迄今为止效率,键=状态{摆烂,摸鱼,正常,高强度},值=天数
local studyDays = {} -- 每个条目各自学习了几天
local linesCount = {} -- 每个条目各自敲了几开始行,键=条目,值=行数
local startDay = nil -- 从什么时候开始
local endDay = nil -- 从什么时候结束
local tmpDay = nil -- 当前记录前一个记录的日子 -- 加载记录集
dofile("life.lua") -- 记录迭代器,逐条返回记录
function getRecords()
local i = #life
return function(s, c)
if i > 0 then
i = i - 1
return life[i + 1]
end
return nil
end, nil, nil
end -- 根据每条记录更新 startDay 跟 endDay
-- 计算前一条记录与后一条记录的间隔天数,统计至levelDays并进行scores每日遗忘操作
function updateTime(date)
local y, m, d = string.match(date, "(%d+)年(%d+)月(%d+)")
if not startDay then
startDay = {
year = y,
month = m,
day = d
}
end
tmpDay = tmpDay or startDay
endDay = {
year = y,
month = m,
day = d
}
-- 计算前一条记录与后一条记录的间隔天数 sep
local sep = os.difftime(os.time(endDay), os.time(tmpDay)) // 3600 // 24
-- 间隔大于 1 天,将sep - 1的天数归入摆烂,并进行每日遗忘操作
if sep > 1 then
updateLevelDays(1, sep - 1)
dailyScores(sep)
else
dailyScores(1)
end
tmpDay = endDay
end -- 更新 linesCount
function updateLinesCount(item, count)
linesCount[item] = (linesCount[item] or 0) + count
end -- 更新 studyDays
function updateStudyDays(item)
studyDays[item] = (studyDays[item] or 0) + 1
end -- 每日遗忘
function dailyScores(n)
for k, v in pairs(scores) do
if v <= 80 then
v = v - 1 * (n or 1)
elseif v <= 100 then
v = v - 2 * (n or 1)
end
scores[k] = v <= 0 and 0 or v
end
end -- 根据 linesCount 对比得出状态{摆烂,摸鱼,正常,高强度},在updateLinesCount之后执行
function level(item, count)
local avg = nil
if studyDays[item] < 4 then -- 不足4天默认按500行计算
avg = 500
else
avg = linesCount[item] // studyDays[item]
end
if count >= avg * 1.25 then
return 4
elseif count >= avg * 0.75 then
return 3
elseif count >= avg * 0.25 then
return 2
elseif count >= avg * 0.1 then
return 1
else
return 0
end
end -- 更新 levelDays, level = 1~4
function updateLevelDays(level, sep)
levelDays[level] = (levelDays[level] or 0) + (sep or 1)
end -- 根据当天写的代码更新 levelDays
function updateLevelDaysByCountLines(countLines)
local totalLines = 0
local totalDays = 0
for _, v in pairs(linesCount) do
totalLines = v + totalLines
end
for _, v in pairs(studyDays) do
totalDays = v + totalDays
end
local avgLines = totalLines // totalDays
if totalDays < 4 then -- 总天数不足4天按500行算
avgLines = 500
end
if countLines > 1.25 * avgLines then
updateLevelDays(4)
elseif countLines > 0.5 * avgLines then
updateLevelDays(3)
elseif countLines > 0.1 * avgLines then
updateLevelDays(2)
else
updateLevelDays(1)
end
end -- 根据条目键值对加分,并更新linesCount
function updateScores(item, count, level)
scores[item] = scores[item] or 40
if level then
if level == 4 then
scores[item] = scores[item] + 4
elseif level == 3 then
scores[item] = scores[item] + 3
elseif level == 2 then
scores[item] = scores[item] + 2
elseif level == 1 then
scores[item] = scores[item] + 1
else
-- do nothing
end
scores[item] = scores[item] > 100 and 100 or scores[item]
else
scores[item] = scores[item] + count * 2
scores[item] = scores[item] > 100 and 100 or scores[item]
end
end -- 根据每条记录更新 scores levelDays linesCount
function update(record)
local countLines = 0 -- 当天写过的代码行数总和
for item, v in pairs(record) do
-- 判断是编程语言还是基础理论
if type(v[1]) == "number" then
local total = v[1] + (v[2] or 0)
countLines = countLines + total
updateLinesCount(item, total)
updateStudyDays(item)
local level = level(item, total)
updateScores(item, total, level)
elseif type(v[1]) == "string" then
updateScores(item, #v)
updateStudyDays(item)
else
error("条目格式有错", 2)
end
end
-- 更新 levelDays , 保存当天状态 {摆烂,摸鱼,正常,高强度}
updateLevelDaysByCountLines(countLines)
end -- 主程序
for r in getRecords() do
-- 更新 startDay 跟 endDay
updateTime(r["date"])
-- 删除"date"减少多余的判断
r["date"] = nil
-- 更新 scores levelDays linesCount
update(r)
end
-- 根据系统时间更新一次时间
updateTime(os.date("%Y年%m月%d日")) -- 排个序 scores,linesCount
scoresSort = {}
for k, v in pairs(scores) do
scoresSort[#scoresSort + 1] = {k, v}
end
linesCountSort = {}
for k, v in pairs(linesCount) do
linesCountSort[#linesCountSort + 1] = {k, v}
end
table.sort(scoresSort, function(a, b)
return a[2] > b[2] -- 熟练度分数对比
end)
table.sort(linesCount, function(a, b)
return a[2] > b[2] -- 总行数对比
end) -- 输出结果
io.output(io.open("output.md", "w"))
-- 做前置拼接,一般是个人介绍的内容
pcall(function()
f = io.open("head.md", "r")
if f then
io.write(f:read("a"))
io.write "\n"
f:close()
end
end)
-- 输出各个表格
io.write("# IT技术熟练度\n")
io.write(string.format("**Time : %d/%d/%d ~ %d/%d/%d**\n", startDay.year, startDay.month, startDay.day, endDay.year,
endDay.month, endDay.day))
io.write("|技术|学习天数|熟练度 (0~100)|程度|评级|\n")
io.write("|------|----|----|----|----|\n")
local cd = {"遗忘", "生疏", "了解", "熟悉", "熟练"}
for i, v in ipairs(scoresSort) do
local stars = ""
for i = 1, v[2] // 20 + 1, 1 do
stars = stars .. ""
end
io.write(string.format("\z
|%s|%s|%d|%s|%s|\n\z
\z", v[1], studyDays[v[1]], v[2], cd[v[2] // 20 + 1], stars))
end
io.write "\n"
io.write("# 这一生写过的代码\n")
io.write("|我的一生|总共行数|天数|平均行数\n")
io.write("|------|----|----|----|\n")
for i, v in ipairs(linesCountSort) do
io.write(string.format("|%s|%d|%d|%d|\n", v[1], v[2], studyDays[v[1]], v[2] // studyDays[v[1]]))
end
io.write "\n"
io.write("# 这一生所处的状态\n")
io.write("|我的状态|天数|\n")
io.write("|------|----|\n")
io.write(string.format("|加班狂魔|%d|\n", levelDays[4]))
io.write(string.format("|正常上班|%d|\n", levelDays[3]))
io.write(string.format("|天天摸鱼|%d|\n", levelDays[2]))
io.write(string.format("|开摆去咯|%d|\n", levelDays[1]))
io.write("\n")
io.write("# 这一生活动的统计\n")
io.write("使用插件 `VS Code Counter` 进行每日统计,仅统计有效行,不含空行\n")
io.write("```lua\n")
f = io.open("life.lua")
io.write(f:read("a"))
f:close()
io.write("```\n")
io.output():close()

[Lua] IT技术熟练度生成器 | 根据IT活动记录生成md表格 | 自创的更多相关文章

  1. c语言活动记录-图解(一)

    来源: 1.<代码揭秘>第六章函数与函数调用 2.http://blog.csdn.net/zhuliting/article/details/6839233 引入话题: 局部变量是动态分 ...

  2. .NET应用架构设计—适当使用活动记录模式代替领域模型模式

    阅读目录: 1.背景介绍 2.简单介绍领域模型模式.活动记录模式 3.活动记录模式的简单示例及要点 4.总结 1.背景介绍 对软件开发方法论有兴趣的博友应该发现最近“领域驱动设计”慢慢的被人发现被人实 ...

  3. 函数调用堆栈及活动记录 堆栈溢出 stack overflow

    小结: 1.当被调函数返回主调函数时,被调函数的 活动记录-activation record / 堆栈帧-stack frame 被 弹出-popping 程序执行栈-program executi ...

  4. 架构模式数据源模式之:表数据入口(Table Data Gateway)、行数据入口(Row Data Gateway)、活动记录(Active Record)

    一:表数据入口(Table Data Gateway) 表数据入口提供了用于访问单个表或者视图(也包含了联表查询)的所有SQL,通常一个表一个类.其它代码通过它来实现对数据库的交互.基于这个特点,表数 ...

  5. yii学习笔记(6),连接数据库,创建活动记录类

    创建数据库用于测试 配置数据库连接 打开yii的配置文件目录下的数据库配置文件config/db.php <?php return [ 'class' => 'yii\db\Connect ...

  6. C语言过程活动记录

    C 语言自动提供的服务之一就是跟踪调用链——哪些函数调用了哪些函数,当下一个return语句执行后,控制将返回何处等.解决这个问题的经典机制是堆栈中的活动记录. 当每个函数被调用时,都会产生一个过程记 ...

  7. SpringBoot与MybatisPlus整合之活动记录(十五)

    活动记录和正常的CRUD效果是一样的,此处只当一个拓展,了解即可 pom.xml <dependencies> <dependency> <groupId>org. ...

  8. Active Record 活动记录

    ActiveRecord活动记录类 一.声明AR类(模型层) namespaceapp\models; useyii\db\ActiveRecord; classCustomer extends Ac ...

  9. cocos进阶教程(3)Lua加密技术

    如果开发者不想让游戏中的资源或脚本文件轻易的暴露给其他人,一般会采用对文件进行加密的方式来保护文件或资源被盗用.Quick-Cocos2d-x 为开发者提供了xxtea加密算法,用来对脚本文件及资源进 ...

  10. lua向文件中写入数据,进行记录

    function readfile(path) local file = io.open(path, "r") if file then local content = file: ...

随机推荐

  1. 2020-12-13:用最少数量的线程,每个线程执行for的空循环,把cpu打满了。如果在for的空循环里添加打印输出函数,会把cpu打满吗?为什么?

    福哥答案2020-12-13:不会.输出会进行io操作,相对于CPU的速度,这是一个非常缓慢的过程,所以CPU会有机会空闲下来.***[评论](https://user.qzone.qq.com/31 ...

  2. 2022-03-25:给定一个长度为 N 的字符串 S,由字符‘a‘和‘b‘组成,空隙由 ‘?‘ 表示。 你的任务是用a字符或b字符替换每个间隙, 替换完成后想让连续出现同一种字符的最长子串尽可能短。

    2022-03-25:给定一个长度为 N 的字符串 S,由字符'a'和'b'组成,空隙由 '?' 表示. 你的任务是用a字符或b字符替换每个间隙, 替换完成后想让连续出现同一种字符的最长子串尽可能短. ...

  3. 【HDU】1312 Red andBlack (DFS&BFS经典好题)

    Red and Black 题目 我是题目链接 题解 找出所能到达的所有黑色的数量,用DFS和BFS均可. BFS: #include <iostream> #include <qu ...

  4. AcWing 1024. 装箱问题

    有一个箱子容量为 V,同时有 n 个物品,每个物品有一个体积(正整数). 要求 n 个物品中,任取若干个装入箱内,使箱子的剩余空间为最小. 输入格式 第一行是一个整数 V,表示箱子容量. 第二行是一个 ...

  5. es笔记五之term-level的查询操作

    本文首发于公众号:Hunter后端 原文链接:es笔记五之term-level的查询操作 官方文档上写的是 term-level queries,表义为基于准确值的对文档的查询,可以理解为对 keyw ...

  6. Abstract Factory Pattern 抽象工厂模式简介与 C# 示例【创建型】【设计模式来了】

    〇.简介 1.什么是抽象工厂模式? 一句话解释:   通过对抽象类和抽象工厂的一组实现,独立出一系列新的操作,客户端无需了解其逻辑直接访问. 抽象工厂模式(Abstract Factory Patte ...

  7. 利用jira及confluence的API进行批量操作(查找/更新/导出/备份/删除等)

    前言: 近期因为某些原因需要批量替换掉 jira 和 confluence中的特定关键字,而且在替换前还希望进行备份(以便后续恢复)和导出(方便查看)atlassian官方的api介绍文档太简陋,很多 ...

  8. 使用 coding.net 发布你的个人博客

    微信文章不允许外链,本文章的静态示例站点,可在文章左下角 "阅读原文" 进行预览. 很多人喜欢在 github pages / gitee pages 发布自己的个人博客,前者由于 ...

  9. ChatGPT 时代,程序员的生存之道 | 人工智能 AI

    前言 ChatGPT 近期炙手可热,仿佛没有什么问题是它不能解决的.出于对 ChatGPT 的好奇,我们决定探索下它对于前端开发人员来讲,是作为辅助工具多一些,还是主力工具更多一些? 2D 能力测试 ...

  10. 页面status:500,报错 server encountered an internal error that prevented it from fulfilling this request.

    The server encountered an internal error that prevented it from fulfilling this request.服务器遇到了一个内部错误 ...