Re: [问题] 请问我这个程式能用循环做吗?

楼主: nullpointerk (nullPointerK)   2020-04-01 04:06:50
※ 引述《IOP14759 (iop14759)》之铭言:
: 开发平台(Platform): (Ex: Win10, Linux, ...)
: WIN8
: 编译器(Ex: GCC, clang, VC++...)+目标环境(跟开发平台不同的话需列出)
: c++builder
: 额外使用到的函数库(Library Used): (Ex: OpenGL, ...)
: 无
: 问题(Question):
: 想请教此程式如果想写成循环该怎么写?
: 程式码(Code):(请善用置底文网页, 记得排版,禁止使用图档)
: int pcs,ID,count;
: AnsiString bit0,bit1,bit2,bit3,bit4,bit5,bit6,bit7,ID_display;
: //将ID转为2进制的字串
: bit1=(ID&0x02)>>1;
: bit2=(ID&0x04)>>2;
: bit3=(ID&0x08)>>3;
: bit4=(ID&0x10)>>4;
: bit5=(ID&0x20)>>5;
: bit6=(ID&0x40)>>6;
: bit7=(ID&0x80)>>7;
: //////////////////////////////////////////////////////////////////
: if(pcs==1)ID_display=bit7;
: if(pcs==2)ID_display=bit7+bit6;
: if(pcs==3)ID_display=bit7+bit6+bit5;
: if(pcs==4)ID_display=bit7+bit6+bit5+bit4;
: if(pcs==5)ID_display=bit7+bit6+bit5+bit4+bit3;
: if(pcs==6)ID_display=bit7+bit6+bit5+bit4+bit3+bit2;
: if(pcs==7)ID_display=bit7+bit6+bit5+bit4+bit3+bit2+bit1; //最多7个
: /////////////////////////////////////////////////////////////////
: if(count==3){Form1->Label31->Caption=ID_display;} //第三栏id
: if(count==2){Form1->Label19->Caption=ID_display;} //第二栏 id
: if(count==1){Form1->Label1->Caption=ID_display;} //第一栏id
: //////////////////////////////////////////////////////////////////
: 补充说明(Supplement):
: 这是小弟的最近写的,每次读1~7个ID,每个ID随机为1或0
: 我是用字串+字串的方式来显示每个ID分别是1或0
: 因为暂时没有需要太多次循环所以用笨方法一个个判断
: 但是自己知道这方法很笨,如果以后要读更多ID我就无解了
好读(?)网页(?)版:https://pastebin.com/raw/6CGgV6Cx
- 因为不知道 AnsiString 是什么,这里我先使用 std::string 代替,还请见谅
- 示范部分,单纯为了方面看到内容,存入了 1 跟 0 以外的数值,一切以原 PO 的规格为主
id 顺序的部分啊,我就使用递增去作了
首先,我们来看看这段逻辑
if(pcs==1)ID_display=bit7;
if(pcs==2)ID_display=bit7+bit6;
if(pcs==3)ID_display=bit7+bit6+bit5;
if(pcs==4)ID_display=bit7+bit6+bit5+bit4;
if(pcs==5)ID_display=bit7+bit6+bit5+bit4+bit3;
if(pcs==6)ID_display=bit7+bit6+bit5+bit4+bit3+bit2;
if(pcs==7)ID_display=bit7+bit6+bit5+bit4+bit3+bit2+bit1; //最多7个
让我们先把他变成一个函数
std::string unkonwn_logic_a(int pcs, std::string bit0, std::string bit1,
std::string bit2, std::string bit3,
std::string bit4, std::string bit5,
std::string bit6, std::string bit7) {
if (pcs == 1) return bit7;
if (pcs == 2) return bit7 + bit6;
if (pcs == 3) return bit7 + bit6 + bit5;
if (pcs == 4) return bit7 + bit6 + bit5 + bit4;
if (pcs == 5) return bit7 + bit6 + bit5 + bit4 + bit3;
if (pcs == 6) return bit7 + bit6 + bit5 + bit4 + bit3 + bit2;
if (pcs == 7) return bit7 + bit6 + bit5 + bit4 + bit3 + bit2 + bit1;
}
可以观察到,事情似乎有些有趣。来改写一下
std::string unkonwn_logic_a(int pcs, std::string bit0, std::string bit1,
std::string bit2, std::string bit3,
std::string bit4, std::string bit5,
std::string bit6, std::string bit7) {
using namespace std::placeholders;
const auto &rec = std::bind(unkonwn_logic_a, _1, bit0, bit1, bit2, bit3, bit4,
bit5, bit6, bit7);
// TODO pcs 小于等于 0 该做些什么?
if (pcs == 1) return bit7;
if (pcs == 2) return rec(1) + bit6;
if (pcs == 3) return rec(2) + bit5;
if (pcs == 4) return rec(3) + bit4;
if (pcs == 5) return rec(4) + bit3;
if (pcs == 6) return rec(5) + bit2;
if (pcs == 7) return rec(6) + bit1;
// TODO pcs 大于 7 该做些什么?
}
由于是递回结构,我们观察前面两个看看
// 当 pcs 为 1 时,我们取尾巴 1 个
if (pcs == 1) return bit7;
// 当 pcs 为 2 时,我们取尾巴 2 个
if (pcs == 2) return rec(1) + bit6;
因此我们可以 "合理" 推断,当 pcs 为 n 时,我们取尾巴 n 个,这样就完成刚刚的 TODO 了
(当然,合理因人而异,这里只是提供一种可能性而已,毕竟原 PO 并没有提供其他的线索)
std::string unkonwn_logic_a(int pcs, std::string bit0, std::string bit1,
std::string bit2, std::string bit3,
std::string bit4, std::string bit5,
std::string bit6, std::string bit7) {
using namespace std::placeholders;
const auto &rec = std::bind(unkonwn_logic_a, _1, bit0, bit1, bit2, bit3, bit4,
bit5, bit6, bit7);
if (pcs <= 0) return "";
if (pcs == 1) return rec(0) + bit7;
if (pcs == 2) return rec(1) + bit6;
if (pcs == 3) return rec(2) + bit5;
if (pcs == 4) return rec(3) + bit4;
if (pcs == 5) return rec(4) + bit3;
if (pcs == 6) return rec(5) + bit2;
if (pcs == 7) return rec(6) + bit1;
if (pcs == 8) return rec(7) + bit0;
return rec(8);
}
Ok,现在我们来处理应该要是 ID 但是名字却是 bit 的变量们,让我们稍微改变一下写法
std::string unkonwn_logic_a(int pcs, std::string ids) {
using namespace std::placeholders;
const auto &rec = std::bind(unkonwn_logic_a, _1, ids);
if (pcs <= 0) return "";
if (pcs == 1) return rec(0) + ids.at(7);
if (pcs == 2) return rec(1) + ids.at(6);
if (pcs == 3) return rec(2) + ids.at(5);
if (pcs == 4) return rec(3) + ids.at(4);
if (pcs == 5) return rec(4) + ids.at(3);
if (pcs == 6) return rec(5) + ids.at(2);
if (pcs == 7) return rec(6) + ids.at(1);
if (pcs == 8) return rec(7) + ids.at(0);
return rec(8);
}
这个时候,我们会发现几个有趣的数字,0 以及 8。他们用在了递回的参数,以及 ids 的 index 使用。
我们可以很快的观察到 8 这个数字其实就是 ids 的长度,让我们基于这个观察再修改一下
std::string unkonwn_logic_a(int pcs, std::string ids) {
using namespace std::placeholders;
const auto &rec = std::bind(unkonwn_logic_a, _1, ids);
const auto &ids_len = ids.length();
if (pcs <= 0) return "";
if (pcs == 1) return rec(pcs - 1) + ids.at(ids_len - pcs);
if (pcs == 2) return rec(pcs - 1) + ids.at(ids_len - pcs);
if (pcs == 3) return rec(pcs - 1) + ids.at(ids_len - pcs);
if (pcs == 4) return rec(pcs - 1) + ids.at(ids_len - pcs);
if (pcs == 5) return rec(pcs - 1) + ids.at(ids_len - pcs);
if (pcs == 6) return rec(pcs - 1) + ids.at(ids_len - pcs);
if (pcs == 7) return rec(pcs - 1) + ids.at(ids_len - pcs);
if (pcs == 8) return rec(pcs - 1) + ids.at(ids_len - pcs);
return rec(8);
}
哎呀,似乎发现了不得了的事情呢。这次,让我们摆脱 8 的诅咒,使用 ids 的长度来处理他
std::string unkonwn_logic_a(int pcs, std::string ids) {
using namespace std::placeholders;
const auto &rec = std::bind(unkonwn_logic_a, _1, ids);
const auto &ids_len = ids.length();
if (pcs <= 0) return "";
if (pcs > ids_len) return rec(ids_len);
return rec(pcs - 1) + ids.at(ids_len - pcs);
}
递回... 递回... 递... 回... fold...
由于字串的相加具有结合律,(sa + sb) + sc = sa + (sb + sc),我们可以将原本的 right fold
改以 left fold 实作,刚好 std::accumulate 就是 left fold
// 这个是小帮手.
std::vector<int> range(int from, int to) {
std::vector<int> ret{};
for (int i = from; i <= to; i++) {
ret.emplace_back(i);
}
return ret;
}
std::string unkonwn_logic_a(int pcs, std::string ids) {
using namespace std::placeholders;
const auto &rec = std::bind(unkonwn_logic_a, _1, ids);
const auto &ids_len = ids.length();
if (pcs <= 0) return "";
if (pcs > ids_len) return rec(ids_len);
const auto &pcs_range = range(1, pcs);
return std::accumulate(pcs_range.cbegin(), pcs_range.cend(),
std::string{},
[&](std::string acc, int cur_pcs) {
acc += ids.at(ids_len - cur_pcs);
return std::move(acc);
});
}
最后,我们可以使用 for loop 去表达 left fold。不知道大家的 for loop 除了 left fold,还
用来表达了那些概念呢? 感觉会不小心搞不清楚是在 for 什么呢
std::string unkonwn_logic_a(int pcs, std::string ids) {
using namespace std::placeholders;
const auto &rec = std::bind(unkonwn_logic_a, _1, ids);
const auto &ids_len = ids.length();
if (pcs <= 0) return "";
if (pcs > ids_len) return rec(ids_len);
auto acc = std::string{};
for (int cur_pcs = 1; cur_pcs <= pcs; cur_pcs++) {
acc += ids.at(ids_len - cur_pcs);
}
return acc;
}
可以针对 for loop 的特性做一些微调,让程式码更简洁,以及修改一下型别,让程式更明确
std::string unkonwn_logic_a(size_t pcs, std::string ids) {
const auto &ids_len = ids.length();
pcs = std::min(pcs, ids_len);
auto acc = std::string{};
for (int cur_pcs = 1; cur_pcs <= pcs; cur_pcs++) {
acc += ids.at(ids_len - cur_pcs);
}
return acc;
}
好了,现在可以回答原 PO 的问题:想请教此程式如果想写成循环该怎么写?
嗯,大概就是这样
诶,等等,你说那些 bit operation 怎么不见了,好吧,那让我们处理处理。
首先,我们修改一下 unkonwn_logic_a 取得长度跟取 index 的形式,新增几个小帮手
std::string::size_type length(const std::string &s) { return s.length(); }
std::string::value_type at(const std::string::size_type pos,
const std::string &s) {
return s.at(pos);
}
namespace std {
std::string to_string(const std::string::value_type c) { return {c}; }
} // namespace std
std::string unkonwn_logic_a(size_t pcs, std::string ids) {
const auto &ids_len = length(ids);
pcs = std::min(pcs, ids_len);
auto acc = std::string{};
for (int cur_pcs = 1; cur_pcs <= pcs; cur_pcs++) {
acc += std::to_string(at(ids_len - cur_pcs, ids));
}
return acc;
}
接着把 unkonwn_logic_a 改为 template 的形式 (小祕密,函数实作根本一样)。
这里比较麻烦的是决定 pcs 的型别,详细可以查查 decltype 跟 declval 的用法,这里不赘述。
template <typename IDS>
std::string unkonwn_logic_a(decltype(length(std::declval<IDS>())) pcs,
IDS &&ids) {
const auto &ids_len = length(ids);
pcs = std::min(pcs, ids_len);
auto acc = std::string{};
for (decltype(pcs) cur_pcs = 1; cur_pcs <= pcs; cur_pcs++) {
acc += std::to_string(at(ids_len - cur_pcs, ids));
}
return acc;
}
那假设我们想要用 uint8_t 去存 ids,就帮 uint8_t 实作 length, at 跟 std::to_string
就好了
size_t length(const uint8_t &b) { return sizeof(b) * 8; }
uint8_t at(const size_t pos, const uint8_t &b) {
return (b & (1 << pos)) ? 1 : 0;
}
namespace std {
std::string to_string(const uint8_t b) { return std::to_string((unsigned){b}); }
} // namespace std
完成后,下面的程式码就都可以运作了,在浏览器上跑跑看吧
https://godbolt.org/z/wrd7Rv
这样就优雅解决原 PO 的烦恼了:如果以后要读更多ID我就无解了
对于通用的需求,我们可以使用 template 版本的 unkonwn_logic_a,只要帮存 id 的结构实作
length, at 跟 std::to_string 就可以。而对于不喜欢 template 版本的人,也可以重新写一份专
属于特定型别的 unkonwn_logic_a,没问题的。
最后一段那个 count 跟 label 的我实在看不懂,请让我无视他。
作者: s4300026 (s4300026)   2020-04-01 08:26:00
推荐这篇文章。
作者: LPH66 (-6.2598534e+18f)   2020-04-01 08:46:00
推一个
作者: cuteSquirrel (松鼠)   2020-04-01 19:59:00
好强
作者: NciscalA   2020-04-02 11:33:00
推。
作者: loveme00835 (发箍)   2020-04-02 20:28:00
xD 好多洞恐怖在做 lifetime extension 的时候确保你的函式是什么行为会比较好喔,而且看来没有好好利用 ADL 而选择把东西直接丢进 std 命名空间这污染不一般,decltype() 虽然说很方便,但你还是没有避掉隐式转型,所以大部分用 auto/decltype 的情境都很多余通常我们在模板化的时候会尽量使用 STL 常见的接口,所以呼叫std::string::size() 会是比较好的选项,如此未来要换成 std::string_view / std::span 都是可行的选项,不然这个模板就是割鸡用牛刀的范例,priority 因此降低了。另外应该是为了避掉编译错误才把参数宣告成 ref to const uint8_t 还有 const uint8_t 吧?整份扣看起来很糟糕
作者: sarafciel (Cattuz)   2020-04-06 01:03:00

Links booklink

Contact Us: admin [ a t ] ucptt.com