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