记一次递归在我项目中所发挥的作用
栏目: JavaScript · 发布时间: 5年前
内容简介:在最近的项目中,有这么一个功能点,就是要获取在WEB IDE里用户所写的注释中的一段特殊规则,然后解析成一段JS config 对象例如:要转成
在最近的项目中,有这么一个功能点,就是要获取在WEB IDE里用户所写的注释中的一段特殊规则,然后解析成一段JS config 对象
例如:
//% width="100px" height="200px" //% pos.top="50px" pos.left="50px" //% writable=true //% q.b.d.w.r.f=30 q.b.d.w.r.a=40 复制代码
要转成
{ width: '100px', height: '200px', pos: { top: '50px', left: '50px' }, writable: true, q: { b: { d: { w: { r: { f: 30, a: 40 } } } } } } 复制代码
类似的规则
什么是递归
来自百度的解释:
递归: 程序调用自身的编程技巧称为递归 (recursion) 尾递归: 如果一个函数中所有递归形式的调用都出现在函数的末尾,我们称这个递归函数是尾递归的。
就是 复读机
递归怎么写?
一般
const fibonacci = num => (num === 1 ? 1 : num * fibonacci(num - 1)) 复制代码
尾递归
const fibonacci = (num, total = 1) => (num === 0 ? total : fibonacci(num - 1, num * total)) 复制代码
Array.reduce
const getArray = count => Array.from({ length: count }, (value, key) => key) const fibonacci = num => getArray(num).reduceRight((accumulator, currentValue) => accumulator * currentValue) 复制代码
功能实现
分步实现
- 过滤常规内容,获取特殊的备注信息,去除空格,并且转成数组
- 此时的数组内容为
[ '//% blockId="sloth_servo_write" block="set servo %channel|degree %degree"', '//% advanced=true', '//% weight=50', '//% degree.min=0 degree.max=180', '//% channel.fieldEditor="gridpicker" channel.fieldOptions.columns=4', '//% a.b.c.d=20 a.b.c.e=222', '//% q.b.d.w.r.f=30 q.b.d.w.r.a=40' ] 复制代码
const code = ` //% width="100px" height="200px" //% pos.top="50px" pos.left="50px" //% writable=true //% q.b.d.w.r.f=30 q.b.d.w.r.a=40` // 获取特殊注释数组 const annotation_array_filter = annotation_item => annotation_item.indexOf('//%') >= 0; // 去除特殊注释前后的空格 const annotation_array_remove_space = annotation_item => annotation_item.trim(); const annotation_array = code.split('\n') .filter(annotation_array_filter) .map(annotation_array_remove_space) 复制代码
- 此时的数组内容为
- 遍历特殊规则数组,把每一项配置都压入一个新对象
- 此时的新对象内内容为
{ a.b.c.d: 20, a.b.c.e: 222, advanced: true, block: "set servo %channel|degree %degree", blockId: "sloth_servo_write", channel.fieldEditor: "gridpicker", channel.fieldOptions.columns: 4, degree.max: 180, degree.min: 0, q.b.d.w.r.a: 40, q.b.d.w.r.f: 30, weight: 50, } 复制代码
- 此时的新对象内内容为
const annotation_array_loop = annotation_item => { // 把注释中的每一项配置转成对象 const result_forEach = result_item => { let annotation_sub_object = {}; // 如果特殊注释数组中的每一项包含多个配置,则扁平化 const array_flattened = data => { const is_array = (this.type(data) === '[object Array]'); const object_recursion = () => { const [key, value] = data.split('='); const annotation_sub_object = {}; try { annotation_sub_object[key] = JSON.parse(value); } catch (error) { annotation_sub_object[key] = JSON.parse(value + '"') }; annotation_object = { ...annotation_object, ...annotation_sub_object }; }; // 判断注释数组项中每一个元素是否有多个配置,如果有则递归,否则则注入对象 is_array ? data.forEach(e => { array_flattened(e); }) : object_recursion(); }; array_flattened(result_item); }; // 去除特殊数组中每一项多余的内容 const result_map = result_item => (result_item.match(/\=/g).length > 1 ? result_item.split(' ') : result_item); const result = annotation_item.replace('//% ', '') .split('/\" /g') .map(result_map); result_forEach(result); }; let annotation_object = {}; // 承载每一个配置的对象 annotation_array.forEach(annotation_array_loop); 复制代码
- 把数组里的元素转成对象
- 此时数组内容为
[ { blockId: "sloth_servo_write" }, { advanced: true }, ... ] 复制代码
- 此时数组内容为
let main_array = []; // 承载每一个配置的数组 const annotation_object_keys = Object.keys(annotation_object); // 获取扁平化后的注释对象的key const annotation_object_keys_loop = annotation_object_key => { // 循环变量每一项注释 const annotation_object_key_array = annotation_object_key.split('.'); // 把多级对象转成数组 const annotation_object_value = annotation_object[annotation_object_key]; // 获取每一项元素的值 let sub_object = {}; // 暂时承载配置对象的对象 const key_reduce = (accumulator, current_value, current_index, array) => { // key值递归,对每一项配置进行合并 if (current_index === 0) { // 如果当前遍历的元素为第一项,也就是说为配置的顶级对象,所以直接压入对象,并且输出 sub_object[current_value] = (current_index === array.length - 1 ? annotation_object_value : {}); return sub_object[current_value]; } accumulator[current_value] = {}; // 如果当前遍历的元素不为第一项,则当前对象元素变为对象 if (current_index === array.length - 1) { // 如果当前遍历的元素为数组最后一项,说明是配置对象最底的元素,可以直接赋值 accumulator[current_value] = annotation_object_value; } return accumulator[current_value]; }; let level_object = annotation_object_key_array.reduce(key_reduce, annotation_object_key_array[0]); level_object = undefined; // 清空level_object main_array.push(sub_object); sub_object = undefined; // 清空sub_object } annotation_object_keys.forEach(annotation_object_keys_loop); 复制代码
- 递归合并对象
- 此时的对象为
{ a: {b: {…}}, advanced: true, block: "set servo %channel|degree %degree", blockId: "sloth_servo_write", channel: {fieldEditor: "gridpicker", fieldOptions: {…}}, degree: {min: 0, max: 180}, q: {b: {…}}, weight: 50 } 复制代码
- 此时的对象为
const annotation_tree = {}; const tree_data = (key, value, object) => { // 递归合并对象 if (this.type(value) !== '[object Object]') { // 如果当前传入元素为对象,则直接压入对象中 object[key] = value; } else { // 否则继续递归 if (!object[key]) { object[key] = {}; }; for (let item in value) { tree_data(item, value[item], object[key]); } }; }; const main_array_forEach = item => { // 循环遍历配置数组 const key = Object.keys(item)[0]; const value = Object.values(item)[0]; tree_data(key, value, annotation_tree); }; main_array.forEach(main_array_forEach); main_array = undefined; // 清空main_array 复制代码
完整代码
// 代码转换器 ((wid, dcm) => { 'use strict'; const win = wid; const doc = dcm; // 基础信息 const base_info = { 'version': '0.0.1', 'author': 'kris', }; // 输出的函数 const funcs = { annotation_parser (annotation) { // 配置树初始化 this.annotation_tree = {}; // 获取特殊注释数组 const annotation_array_filter = annotation_item => annotation_item.indexOf('//%') >= 0; // 去除特殊注释前后的空格 const annotation_array_remove_space = annotation_item => annotation_item.trim(); // 循环遍历特殊注释数组 const annotation_array_loop = annotation_item => { // 把注释中的每一项配置转成对象 const result_forEach = result_item => { let annotation_sub_object = {}; // 如果特殊注释数组中的每一项包含多个配置,则扁平化 const array_flattened = data => { const is_array = (this.type(data) === '[object Array]'); const object_recursion = () => { const [key, value] = data.split('='); const annotation_sub_object = {}; try { annotation_sub_object[key] = JSON.parse(value); } catch (error) { annotation_sub_object[key] = JSON.parse(value + '"') }; annotation_object = { ...annotation_object, ...annotation_sub_object }; }; // 判断注释数组项中每一个元素是否有多个配置,如果有则递归,否则则注入对象 is_array ? data.forEach(e => { array_flattened(e); }) : object_recursion(); }; array_flattened(result_item); }; // 去除特殊数组中每一项多余的内容 const result_map = result_item => (result_item.match(/\=/g).length > 1 ? result_item.split(' ') : result_item); const result = annotation_item.replace('//% ', '') .split('/\" /g') .map(result_map); result_forEach(result); }; let annotation_object = {}; // 承载每一个配置的对象 annotation.filter(annotation_array_filter) .map(annotation_array_remove_space) .forEach(annotation_array_loop); let main_array = []; // 承载每一个配置的数组 const annotation_object_keys = Object.keys(annotation_object); // 获取扁平化后的注释对象的key const annotation_object_keys_loop = annotation_object_key => { // 循环变量每一项注释 const annotation_object_key_array = annotation_object_key.split('.'); // 把多级对象转成数组 const annotation_object_value = annotation_object[annotation_object_key]; // 获取每一项元素的值 let sub_object = {}; // 暂时承载配置对象的对象 const key_reduce = (accumulator, current_value, current_index, array) => { // key值递归,对每一项配置进行合并 if (current_index === 0) { // 如果当前遍历的元素为第一项,也就是说为配置的顶级对象,所以直接压入对象,并且输出 sub_object[current_value] = (current_index === array.length - 1 ? annotation_object_value : {}); return sub_object[current_value]; } accumulator[current_value] = {}; // 如果当前遍历的元素不为第一项,则当前对象元素变为对象 if (current_index === array.length - 1) { // 如果当前遍历的元素为数组最后一项,说明是配置对象最底的元素,可以直接赋值 accumulator[current_value] = annotation_object_value; } return accumulator[current_value]; }; let level_object = annotation_object_key_array.reduce(key_reduce, annotation_object_key_array[0]); level_object = undefined; // 清空level_object main_array.push(sub_object); sub_object = undefined; // 清空sub_object } annotation_object_keys.forEach(annotation_object_keys_loop); const tree_data = (key, value, object) => { // 递归合并对象 if (this.type(value) !== '[object Object]') { // 如果当前传入元素为对象,则直接压入对象中 object[key] = value; } else { // 否则继续递归 if (!object[key]) { object[key] = {}; }; for (let item in value) { tree_data(item, value[item], object[key]); } }; }; const main_array_forEach = item => { // 循环遍历配置数组 const key = Object.keys(item)[0]; const value = Object.values(item)[0]; tree_data(key, value, this.annotation_tree); }; main_array.forEach(main_array_forEach); main_array = undefined; // 清空main_array }, }; // 引用的资源 const libs = {}; // 工具函数 const tools = { // 获取元素类型 type (object) { return Object.prototype.toString.call(object); }, // 分离传入的代码跟配置 separate_code_and_config (data) { data.split('\n') .forEach(item => { item.indexOf('//%') >= 0 ? this.blockly_config_array.push(item.trim()) : this.python_code_array.push(item); }); }, }; // 定义的元素 const vars = { blockly_config_array: [], python_code_array: [], annotation_tree: {}, python_tree: {}, }; // 根对象 const code_transformer = { ...base_info, ...libs, ...funcs, ...tools, ...vars, }; const _global = (() => { return this || (0, eval)('this'); })(); if (typeof module !== 'undefined' && module.exports) { module.exports = code_transformer; } else if (typeof define === 'function' && define.amd) { define([], function () { return code_transformer; }); } else { !('code_transformer' in _global) && (_global.code_transformer = code_transformer); }; })(window, document); 复制代码如果你、喜欢探讨技术,或者对本文有任何的意见或建议,你可以扫描下方二维码,关注微信公众号“
”,随时与鱼头互动。欢迎!衷心希望可以遇见你。
以上所述就是小编给大家介绍的《记一次递归在我项目中所发挥的作用》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- FPGA将在云端数据中心业务发挥突出的作用
- 数据挖掘如何在互联网金融风险控制中发挥作用
- 漏洞扫描“全覆盖”法则 | 被动扫描如何在资产发现中发挥作用?
- Web3基金会资助的100个项目各自发挥什么作用?
- 全球机器学习“教父”Tom Mitchell教授:未来十年,AI将发挥重要作用|钛媒体全球科技月
- 高峰对话:发挥数据价值,引导传统产业转型升级
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。