Showing posts with label QBasic. Show all posts
Showing posts with label QBasic. Show all posts

Saturday, 1 November 2025

The year I finally achieved that JLPT certification

Some say "better late than never". I got a huge dose of that this year as I obtained my Japanese Language Proficiency Test (JLPT) N5 Certificate.

Why was this overdue? Well, you see, because I actually began on this journey at the tender age of 15. I was a Polytechnic student at the time, and this was a supplementary module. At first, things went well. Having received education in Chinese, learning the basic sounds of Japanese was a breeze. Learning the Hiragana and Katakana writing systems wasn't much of a stretch as well; in fact it was significantly easier than reading and writing Chinese characters. Until we got to Kanji, which essentially was Chinese characters.

Ugh, more Chinese.

At which point I lost all interest.

Also, at this time, I had begun an obsession with writing code, and building stuff. Japanese was a pretty language, but it just didn't fit into my world the same way C++, SQL or even QBasic, did.

And the rest, as they say, is history. Even after graduation, not only did I neglect whatever little Japanese I had learned, the next few decades were a disastrous collection of bad career choices, even worse lifestyle choices, and sobering life lessons.

COVID-19 and Clubhouse

Fast-forward to 2021. COVID-19 had the world in its loathsome grip and I, like many others, sought human interaction online. That was when I discovered the Clubhouse app. Through it, I encountered numerous communities, not all of which spoke English, Mandarin, or even Cantonese.

A few of these were Japanese speakers. And some were even offering Japanese lessons. I dove right in, relearning all the basics I had forgotten. Many times, I would simply listen in, try to make sense of the conversation, and Google words that came up. Then I started engaging, hesitantly, in the conversations. I even participated in Mandarin-Japanese exchanges!

Little cultural exchanges.

It was slow going. I wrote an app to facilitate relearning Hiragana and Katakana. I practiced writing every day, religiously. But there was this feeling that I could do more.

That was when someone suggested that I take the JLPT. And from that day forth, I took things up a notch.

Training in earnest

My next step was to install Duolingo on my phone. For the next few years, I faithfully partook of the exercises daily. At the same time, I went onto YouTube to search for JLPT Listening exercises. I didn't really have a sense of how far I had progressed; I just knew I needed to keep going. I've never been brilliant; but what I am good at is being consistent.

One year later, I downloaded the Migii app, and paid the subscription fees for accessing sample JLPT exam questions. With Duolingo, the returns were diminishing. I needed to train a different set of linguistic muscles - the kind used for passing the exam. Duolingo had brought me to a certain point where I could read the Japanese exam questions without too much difficulty... now I had to practice answering them.

The next milestone was hit another year later when, while watching a Japanese TV show, with English subtitles, I noticed something odd. Almost constantly, the subtitles would read "It's OK". Even though "It's OK" was a perfectly reasonable thing to say in the context of the current point of the story, it was a pretty poor translation.

Phrase Literal Meaning English Translation
daijobu It's OK. "It's OK."
dou itashimashite You're welcome. "It's OK."
shinpai suna Don't worry. "It's OK."
anshin shite Relax. "It's OK."
ki ni shinai de Pay it no mind. "It's OK."


At this point, I found myself thinking - wow, these translators are so goddamn lazy! They just use "It's OK" for everything!

And my next thought was - hey, if I know this much, shouldn't I be taking the JLPT already?!

The JLPT Test Voucher

That was when I registered for the JLPT on the Japanese Cultural Society of Singapore website. and paid a hundred Singapore dollars for the privilege. A month later, the JLPT test voucher arrived in the mail. The train was in motion; there was no going back.

Meanwhile, I continued training religiously. With Listening practice, especially. Online chatter had it that the recordings would be played only once, so I trained myself similarly, by not rewinding and replaying.

The JLPT N5 Exam

It was July when the day arrived. I head for the Singapore Management Institute where the exam was held, and stood in line with what looked like mostly Burmese folks. Interesting.

The invigilator who took charge of the exam room I had been assigned to, seemed to be Japanese going by the way she spoke English.

Shading answers with a pencil.

The proceedings were charmingly old-school. We were given question and answer sheets. It was all multiple-choice, and the correct answer had to be shaded with a pencil for feeding into a scanning machine. Despite my numerous certificates, I hadn't done this since... 2015? This was because my last couple certifications were earned from doing the coursework and presentations, rather than standardized tests.

The toughest moment in the exam came from the Choukai, which was the Listening portion. Despite my best efforts, my concentration slipped at various points. It was with considerable relief that I handed in my question and answer sheets, and headed off.

In August, I logged on to the JLPT portal to check my results. I had passed, and my results were more than decent. I actually scored higher on Listening than other sections!

The real value of all this

Last September, I got my actual physical certification through the mail. It was a foregone conclusion by that time, but I still felt that little thrill of pride. Job well done, bro, I told my reflection. Not such a big deal in the larger picture, but we've got to celebrate our wins even if they're small. Especially if said win took thirty-three friggin' years to achieve.

My results.

The JLPT N5 Certification isn't going to change my life. As far as professional cred goes, it's barely a blip. Achieving the JLPT N5 Certification probably puts me on par with the average Japanese toddler where the language is concerned. As for the value of understanding multiple languages? That's not much of a flex. This is Southeast Asia; just about everyone and their dog is multilingual.

No; at my age, the act of learning is arguably more important than what's being learned. It helps stave off dementia.

The real value is knowing and affirming, that with sufficient motivation and putting in the time and effort, I can learn pretty much whatever I choose to learn. And that is powerful stuff. In an age where things are constantly and rapidly evolving, the ability to learn shit has become vital - not just in the tech industry, but for life itself.

In hindsight, I should have realized this. How did I learn ASP? PHP? Ruby? VueJS? D3? All by picking up a book, watching videos, visiting websites, and constant practice. Most of the time, it really is that simple. The methods vary, but at the end of the day, it's about the willingness to put in the hours.

The linguistic journey continues!

I do want to see if I can achieve the next rung, the JLPT N4 Certification. Again, I'm not really sure why. Just for the hell of it, I guess. It's not like I plan to visit Japan. It's not like I realistically have anything to prove to anyone.

On the other hand, Korean does seem pretty interesting.

Decisions, decisions...

Ganbarimashoo!
T___T

Monday, 29 April 2024

What's the best programming language for beginners?

What should be a programmer's first language?

Often, this is not a deliberate choice, more a matter of circumstance. Perhaps a programmer started on QBasic as a kid. Perhaps, like me, the programmer picked it up in school and was made to learn C++ or Java. I would even venture to say very few programmers start out saying I'm going to begin my programming journey with the best, most modern language out there! To begin with, how would they know what constitutes the "best" programming language, or what even is a good programming language? You don't know what you don't know, after all.

Let's learn!

Also, how suitable a programming language is for your purposes also really depends on what your purpose is. Do you want to crunch data? Write games? Build web portals?

So, obviously, before we can sort out what the "best" programming language is, perhaps it is more useful to figure out what a programmer's first programming language should be. After all, this choice opens different doors.

For that, we should probably look at a few factors. I can only draw upon languages I've coded in before. as examples, obviously.

Syntax

For this, we look at simplicity of writing code in that language. How intuitive the syntax is, and how few extra keystrokes needed to get something done.

For this, my obvious picks would be Python or Ruby. Less of the semi-colons and curly brackets. Anyone picking either Python or Ruby as a first language, I suspect, would be less likely to give up out of frustration.

Code syntax.

C# and Java is probably among the worst for this. Too many namespaces. Overly verbose syntax. I haven't had many projects that needed to be done in either of these languages, and for that I'm truly grateful.

But the one that takes the proverbial cake, in terms of being cumbersome, would be PHP. The function calls structures are wildly inconsistent, and in addition to semi-colons and curly brackets, it also requires a dollar sign preceding every variable name.

As far as syntax goes, the group of programming languages with visually similar styles - PHP, Java, JavaScript, C and its variants - is such that if you pick one up, the learning curve for the others, at least for the simple stuff, isn't so great. Thus, even if PHP is hideously cumbersome in parts, it benefits from being visually similar to other languages mentioned.

Transferable skills

This measures underlying principles or skills that one will pick up while programming in a certain language, that will be useful when transitioning to another stack. It could be something as simple as similar syntax (as mentioned earlier) or strong data typing.

Or it could be something so ubiquitous that no matter what stack you're in, you're almost certainly going to encounter it. A great example of this would be SQL. No matter what kind of programming you go into, the chances of you needing to access a database at some point, are pretty good. I wouldn't go so far as to say that this makes SQL the number one choice for a programmer's first language, but it makes a great case for being a programmer's second, at least.

Data processing.

For a similar reason, if one wanted to go into web development, it's almost impossible not to have to deal with HTML, CSS and JavaScript, but learning JavaScript as a first language just for that, is questionable. As a second language, definitely.

Strongly-typed, class-based languages such as Java and C# would be good choices as a first programming language for learning concepts such as Object-Oriented Programming. (PHP and JavaScript kind of implement OOP as well, but in a way that's a little odd). Python would be the choice if one wanted to have a strong foundation in data structures. However, Python's lack of strong typing might work against it as a first language.

Ease of setup

One of the most daunting tasks of learning a language is setting up the environment for it. Java requires the JRE (Java Runtime Environment). PHP requires an Apache server. Python and Ruby, well, you get the idea.

For this, the most fuss-free option has to be JavaScript, hands down.

All major browsers
run JavaScript.

JavaScript runs on all major browsers. You don't need to install anything you probably don't already have. If you're reading this, unless you're reading it on your phone, you have a desktop browser which you can run JavaScript in. It's almost zero setup. Just write your code, and run it in the browser.

For a Windows environment, VBScript is also almost zero effort. All you need to do is write your script, save it with extension "*.vbs" and you can run it by double-clicking it! Unfortunately, I'm not sure how useful knowing VBScript is.

In essence, in the cases of both JavaScript and VBScript, the environment has already been set up for the programmer.

Popular support

No developer plies their trade without consulting a reference of some sort. Textbooks are a valid source, but for the most updated material, we turn to the internet. Portals and forums provide most of these, with each language having large communities of programmers ready to provide support.

Learn by reading, or
by community.

Which programming language has the largest, or best, communities?

That's hard to say. All communities have their fair share of toxic losers who waste little time being condescending and acting like gatekeepers. At the same time, each community also boasts genuine people. I'm not going to waste time detailing how large each programming language's community is relative to others. Suffice to say, they're all (OK, mostly) large enough to be useful.

Generally, the longer they have been around, the larger they tend to be.

Conclusion

Whatever the choice turns out to be, ultimately, there will be benefits to that choice. That is because no matter what language someone chooses for their first programming experience, the fact remains is that they are still engaging in the activity of programming. It's a start. Some starts are better than others, sure, but it's a step. And hopefully the first of many.

Mind your (first) language!
T___T

Sunday, 3 December 2023

Ode to my Lenovo

Two weeks back, my trusty Lenovo Z51-70 laptop gave up the ghost. It was showing signs well before that; consistently dropping my network connection, lagging severely when saving files and most tellingly, a noticeable flicker on the monitor. Can't say it didn't give me fair warning when on Saturday, it refused to power on.

Now, that was a sentimental moment.

Rest well, my friend.

I first obtained this flat white beauty during a Christmas Sale in 2014, when I was about to begin a Specialist Diploma in Mobile Apps Development. While my company at the time had issued me a decent notebook, I didn't feel comfortable using company property for my personal enterprise. I had a personal laptop, but it was on its last legs. Thus, I went for an i5 Intel Core that would meet my programming needs.

2015 to 2017

2015 was the second semester of my Specialist Diploma in Mobile Apps Development. With Java being the operating language, I installed Eclipse and the Java Runtime Environment. I also had NodeJS installed, and wrote apps in jQuery Mobile to deploy onto the mobile phone. This would perhaps be the most serious and intensive period of using the Lenovo.

Mobile app development.

Somewhere along the way, in addition to my steady diet of HTML, CSS and JavaScript experimentation, I experimented with things QBasic, LESS and AngularJS. An odd mix, I know, but I was having fun.

MeteorJS came along for me in early 2017. The lessons I learned here would help me years later when I experimented with ReactJS.

2017 to 2019

This was the period of my life where I settled down into a regular nine-to-five. I still experimented on weekends, writing D3 and ReactJS code. It was also the time I picked up Ruby and Rails, for whatever reason.

Like a stallion.

The Lenovo handled it all like a champ. It was tireless, inexhaustible, like a stallion in its prime.

Around the latter half of this period, I started working on my ACTA. Whatever work I needed done, I did it on my trusty laptop.

At some point during this period, I also got married. Late nights at the console were no longer viable, not if I wanted to sleep in the same bed as my wife. Still, I squeezed in whatever time I could.

2019 to 2023

This was the year the COVID-19 pandemic hit, and marked one of the longest durations my wife and I were geographically separated. During this time, my work on the Lenovo intensified, with me trying out Highcharts and VueJS.

During this period, however, I had acquired a MacBook and was gradually transitioning away from my faithful Lenovo which was starting to show its years.

Getting pretty old.

It was also when I embarked on my yet another Specialist Diploma, this time in Data Analytics. The MacBook handled Python and Tableau, but only my Lenovo could run Spotfire and  Power BI. It creaked and groaned, but ultimately delivered.

Why is this particular laptop special?

Why do I feel so strongly about this fella? It's not the first laptop to die on me, after all. I've lived more than forty years. But the last nine years of my professional life have been pretty tumultuous, and also represented the bulk of my career advancement and tech education, spanning two Specialist Diplomas and a Higher Certificate. This Lenovo was with me through all of it. I played no games on it. All I did was write code, and run code. (Some of that code were games I wrote, but we're just splitting hairs at that point!)

Much of whatever important files I have are saved on the cloud - on Google Drive, GitHub and Trello. Thankfully, I can resume my work on a new machine with little trouble. But I'll always treasure my time with this machine.


Rest In PC,
T___T

Friday, 6 October 2023

TeochewThunder: Year Nine (Part 1/2)

Here it is - the big Nine!

Yes, it has been nine years since I embarked on the improbable journey that was this tech blog. At times, it has felt less like a tech blog and more like a philosophy or even political blog. But I suppose multiple things can be true at the same time. As long as this blog stays true to its mission of documenting my own little tech journey, I think all is well.

Happy ninth birthday!

That aside, the fact that this blog has gone on for nine years, staying on course with the same format as all the preceding years since 2014, is a testament to my consistency. Hey, it's one hell of a moment; I think I'm allowed a little self-congratulations. Still, change is inevitable, and in some cases, I think change can be beneficial. For example, I've been attempting to not swear so fucking much express my views in a less inflammatory and moderate manner, and since less posts now carry the Profanity Alert tag, I seem to have largely succeeded. Room for improvement, though!

AI Image Generation

2023 is probably the year that AI became mainstream, with text and image generators making their runds on the market. I've actually used both. Text generators such as ChatGPT to generate filler text for code, and image generators (sch as Imagine) to produce nice pictures for the blog.

Imagine

In recent years, I've been mostly using stock photos, but sometimes the search is long and the results are underwhelming. Being able to generate pretty much what I need with a reasonable degree of accuracy has proven to be an attraction too great to ignore. The problem with such images, however, it that people often look unrealistic and deformed. Thus, I've tried to limit such image generation to animals, plants and inanimate objects. Anything that absolutely requires human parts in the image, I continue relying on stock photos. I think it's an acceptable compromise.

Tech Content

As is par for the course with other years, much of my repertoire has revolved around HTML, CSS and JavaScript, and occasionally PHP. But true to form, I have been experimenting with other technologies. Not new programming languages, but new software.

Oracle APEX

Low-Code solutions was something I embarked upon after attending Oracle's CloudWorld Tour back in April. Oracle APEX sounded promising at the time, and after some tinkering, I have not found myself overly impressed. However, I have not been thoroughly underwhelmed either. And thus I have actually, as is my wont, used a technology I am learning, to create something I want. The result is last month's web tutorial.

This year, QBasic made the rare appearance in one of my web tutorials. There's quite the story behind that one, but go read the web tutorial if you want to know.

Other Content

There's been a fair bit brewing around the world. The rise of AI, global unrest, and much moving and shaking in big tech. Stuff has happened in Singapore as well, but unless I can somehow tie it to a tech angle, I tend not to feature it on the blog.

Gobal news!

One point of evolution of this blog is that gradually, I have been screening out things that are big enough for me to talk about. If it's about tech, that's an obvious inclusion. If it's not so much about tech but more about Singapore or workplace issues, then it's still relevant as long as I can make it relevant. The rule is that I should not have to try too hard to make it relevant.

I've also gone into the process of tag removal. Over the years, the number of tags that simply were not important enough to warrant being repeated, has grown. In my defense, they seemed important at the time, and it's only with the benefit of hindsight that I realized a substantial amount of cleanup was needed. This is an ongoing process though, so bear with me!

My Equipment

As I write this passage, I am doing it on a creaky and flickering laptop that is undoubtedly on its last legs. This machine has served me since 2015, a few months after I embarked on TeochewThunder. It has been 8 years. Ah, the memories. The support on the Windows version has run out, and I'm dragging my feet on moving on to a new machine.

Thankfully, in 2020, I bought a MacBook Air and the little beauty's got some life in her yet. I expect that soon I will be moving on from presenting screenshots from my Windows machine, to taking screenshots from my MacBook Air.

Failing equipment.

My mobile phone is not what one would call state of the art and is in dire need of an upgrade. Still, thankfully, it seems to have weathered much abuse and should hold out for a while yet. Hopefully, the upcoming blogging break will give me ample time to transition to machines that aren't decrepit to the point of embarrassment.

Next

We examine the highs and lows of the blog.

Thursday, 4 May 2023

Web Tutorial: QBasic Filestitch

It's exceedingly rare that I ever get to use QBasic to solve a work problem. As such, when that happens, I'm inclined to commemorate it in true geek fashion - by writing a web tutorial.

Some context coming up!

When my company last year upgraded to Oracle Cloud, all the POS machines at various outlets were now sending CSV files to the relevant department. The problem was, whereas before the files were already consolidated before the department received it, with this new, ahem, upgraded system, the outlets were now sending the files separately. All twenty of them. As you can imagine, manually stitching the files together daily, quickly became a total pain in the ass.

What I eventually did was use QBasic to write an executable file that would perform the consolidation for them. All they needed to do was double-click on the executable, fill in a bit of information, and an entirely new CSV file would be produced in seconds, or less!

Today, I will show you how to write this code. Of course, because I don't want to give away any company secrets, certain things have been changed. But the idea remains the same!

We first have a series of CSV files. You can access them at my repository. Each of these files are named this way.

tx_{date in "YYYYMMDD" format}_{outlet four-digit id}.csv.

These have the following fields. A sample is shown here. The fields are for date, time, order number, item name, category, quantity and order type.

filestitch/tx_20221210_1122.csv
"2022-12-10", "11:16:12", "100001210015", "Strawberry Deluxe Surprise", "MAIN", "1", "DINE-IN"
"2022-12-10", "11:16:12", "100001210015", "Mango Slurpee", "DRINK", "2", "DINE-IN"
"2022-12-10", "11:18:09", "100001210016", "Chocolate Overload", "MAIN", "2", "TAKE-AWAY"
...


filestitch/tx_20221210_1123.csv
"2022-12-10","10:45:36","444002770055","Mixed Fruit Cheesecake","SIDE","1","DINE-IN"
"2022-12-10","10:45:36","444002770055","Hot Six","MAIN","1","DINE-IN"
"2022-12-10","10:45:36","444002770055","Chocolate Overload","MAIN","1","DINE-IN"
...


filestitch/tx_20221210_1124.csv
"2022-12-10","11:00:02","300005557800","Banana Sandwich Special","MAIN","4","TAKE-AWAY"
"2022-12-10","11:02:58","300005557801","Cream Cake","SIDE","1","TAKE-AWAY"
"2022-12-10","11:03:55","300005557802","Ice Lychee Tea","DRINK","2","TAKE-AWAY"
...


filestitch/tx_20221210_1125.csv
"2022-12-10","10:49:10","200000778889","Blueberry Muffin","SIDE","4","TAKE-AWAY"
"2022-12-10","10:55:11","200000778890","Blueberry Muffin","SIDE","2","TAKE-AWAY"
"2022-12-10","11:20:06","200000778891","Chocolate Overload","MAIN","1","TAKE-AWAY"
...


There are only four files, but in the case of my company, we had to deal with over twenty files. This is how we define each outlet, through the ids and outlets arrays.
CLS

DIM ids(4) AS STRING
DIM outlets(4) AS STRING

ids(0) = "1122"
outlets(0) = "TUAS"
ids(1) = "1123"
outlets(1) = "PASIR RIS"
ids(2) = "1124"
outlets(2) = "BALESTIER"
ids(3) = "1125"
outlets(3) = "LITTLE INDIA"


Here, we ask for the date, and ask for it in a certain format. We assign it to the variable FileDate. This is going to help us identify the correct file.
ids(3) = "1125"
outlets(3) = "LITTLE INDIA"

INPUT "Date of file? (YYYYMMDD format, eg, 20221101)", FileDate$


And then we ask for the directory, and assign it to the variable FileDir. Here, we're going to assume it's in the C drive.
INPUT "Date of file? (YYYYMMDD format, eg, 20221101)", FileDate$
INPUT "Directory?", FileDir$


This is pretty basic (hur hur) so far.




And here, we create a file in the directory specified by FileDir, with the name derived from FileDate. This will be the CSV file we write to. We will define this as #1.
OPEN "C:\" + FileDir$ + "\tx_" + FileDate$ + "_consolidated.csv" FOR OUTPUT AS #1


What we have now is a For loop to iterate through the outlets. As far as I know, there is no graceful way to grab the size of an array in QBasic, so let's just hardcode this. And after everything, we close #1 as a matter of good cleanup practices.
OPEN "C:\" + FileDir$ + "\tx_" + FileDate$ + "_consolidated.csv" FOR OUTPUT AS #1

FOR i = 0 TO 3 STEP 1

NEXT

CLOSE #1


We formulate a filename based on the element in the ids array pointed to by i, FileDate and FileDir. Needless to say, that directory actually has to exist. On the next line, we print FileName for better visibility.
FOR i = 0 TO 3 STEP 1
    FileName$ = "C:\" + FileDir$ + "\tx_" + FileDate$ + "_" + ids(i) + ".csv"
    PRINT FileName$

NEXT


Here, we open the file. It's defined as #2.
FOR i = 0 TO 3 STEP 1
    FileName$ = "C:\" + FileDir$ + "\tx_" + FileDate$ + "_" + ids(i) + ".csv"
    PRINT FileName$
    OPEN FileName$ FOR INPUT AS #2
NEXT


Now, what if that file isn't found or is empty? We trap this condition in an If block, checking if #2 has no data. We use the LOF() function, passing in 2 (for #2)as an argument for this. If it returns 0, either no such file exists or the file is empty.
FOR i = 0 TO 3 STEP 1
    FileName$ = "C:\" + FileDir$ + "\tx_" + FileDate$ + "_" + ids(i) + ".csv"
    PRINT FileName$
    OPEN FileName$ FOR INPUT AS #2
    IF LOF(2) = 0 THEN

    ELSE

    END IF

NEXT


And if so, we print out the name of the outlet (using the element in the outlets array pointed to by i) and use the KILL statement on #2. I know this sounds really violent, but all it does is delete the file, if found.
FOR i = 0 TO 3 STEP 1
    FileName$ = "C:\" + FileDir$ + "\tx_" + FileDate$ + "_" + ids(i) + ".csv"
    PRINT FileName$
    OPEN FileName$ FOR INPUT AS #2
    IF LOF(2) = 0 THEN
        PRINT outlets(i) + " OUTLET HAS NO DATA"
        KILL FileName$
    ELSE

    END IF
NEXT


If there is such a file and it has data, we use a Do-while loop, ending only when the end of the file is reached. This is checked using the EOF() function and passing in 2 (for #2) as an argument.
FOR i = 0 TO 3 STEP 1
    FileName$ = "C:\" + FileDir$ + "\tx_" + FileDate$ + "_" + ids(i) + ".csv"
    PRINT FileName$
    OPEN FileName$ FOR INPUT AS #2
    IF LOF(2) = 0 THEN
        PRINT outlets(i) + " OUTLET HAS NO DATA"
        KILL FileName$
    ELSE
        DO WHILE NOT EOF(2)

        LOOP
        CLOSE #2

    END IF
NEXT


So let's try it with an obviously wrong date and directory. You'll get a few of these, two for each file. Just keep clicking "Yes".




Here, it lists all the files it didn't find, which is all four of them.




Here, we use the INPUT statement to get the data from #2, the current file! After the first argument, it's all the data from the CSV files, separated by commas. Note that it has to be, for obvious reasons, in the same sequence!
FOR i = 0 TO 3 STEP 1
    FileName$ = "C:\" + FileDir$ + "\tx_" + FileDate$ + "_" + ids(i) + ".csv"
    PRINT FileName$
    OPEN FileName$ FOR INPUT AS #2
    IF LOF(2) = 0 THEN
        PRINT outlets(i) + " OUTLET HAS NO DATA"
        KILL FileName$
    ELSE
        DO WHILE NOT EOF(2)
            INPUT #2, COLDATE$, COLTIME$, COLNUM$, COLITEM$, COLCAT$, COLQTY$, COLTYPE$
        LOOP
        CLOSE #2
    END IF
NEXT


Then we have InputStr, which is initialized as an empty string. Then we append all the data to it. Note that we have to use the CHR$() function with 34 as an argument, to add the double-quotes.
FOR i = 0 TO 3 STEP 1
    FileName$ = "C:\" + FileDir$ + "\tx_" + FileDate$ + "_" + ids(i) + ".csv"
    PRINT FileName$
    OPEN FileName$ FOR INPUT AS #2
    IF LOF(2) = 0 THEN
        PRINT outlets(i) + " OUTLET HAS NO DATA"
        KILL FileName$
    ELSE
        DO WHILE NOT EOF(2)
            INPUT #2, COLDATE$, COLTIME$, COLNUM$, COLITEM$, COLCAT$, COLQTY$, COLTYPE$
            InputStr$ = ""
            InputStr$ = InputStr$ + CHR$(34) + COLDATE$ + CHR$(34) + ","
            InputStr$ = InputStr$ + CHR$(34) + COLTIME$ + CHR$(34) + ","
            InputStr$ = InputStr$ + CHR$(34) + COLNUM$ + CHR$(34) + ","
            InputStr$ = InputStr$ + CHR$(34) + COLITEM$ + CHR$(34) + ","
            InputStr$ = InputStr$ + CHR$(34) + COLCAT$ + CHR$(34) + ","
            InputStr$ = InputStr$ + CHR$(34) + COLQTY$ + CHR$(34) + ","
            InputStr$ = InputStr$ + CHR$(34) + COLTYPE$ + CHR$(34) + ","

        LOOP
        CLOSE #2
    END IF
NEXT


We also add this line to write the outlet name into a new column.
FOR i = 0 TO 3 STEP 1
    FileName$ = "C:\" + FileDir$ + "\tx_" + FileDate$ + "_" + ids(i) + ".csv"
    PRINT FileName$
    OPEN FileName$ FOR INPUT AS #2
    IF LOF(2) = 0 THEN
        PRINT outlets(i) + " OUTLET HAS NO DATA"
        KILL FileName$
    ELSE
        DO WHILE NOT EOF(2)
            INPUT #2, COLDATE$, COLTIME$, COLNUM$, COLITEM$, COLCAT$, COLQTY$, COLTYPE$
            InputStr$ = ""
            InputStr$ = InputStr$ + CHR$(34) + COLDATE$ + CHR$(34) + ","
            InputStr$ = InputStr$ + CHR$(34) + COLTIME$ + CHR$(34) + ","
            InputStr$ = InputStr$ + CHR$(34) + COLNUM$ + CHR$(34) + ","
            InputStr$ = InputStr$ + CHR$(34) + COLITEM$ + CHR$(34) + ","
            InputStr$ = InputStr$ + CHR$(34) + COLCAT$ + CHR$(34) + ","
            InputStr$ = InputStr$ + CHR$(34) + COLQTY$ + CHR$(34) + ","
            InputStr$ = InputStr$ + CHR$(34) + COLTYPE$ + CHR$(34) + ","
            InputStr$ = InputStr$ + CHR$(34) + outlets(i) + CHR$(34)
        LOOP
        CLOSE #2
    END IF
NEXT


Finally, we write InputStr into #1.
FOR i = 0 TO 3 STEP 1
    FileName$ = "C:\" + FileDir$ + "\tx_" + FileDate$ + "_" + ids(i) + ".csv"
    PRINT FileName$
    OPEN FileName$ FOR INPUT AS #2
    IF LOF(2) = 0 THEN
        PRINT outlets(i) + " OUTLET HAS NO DATA"
        KILL FileName$
    ELSE
        DO WHILE NOT EOF(2)
            INPUT #2, COLDATE$, COLTIME$, COLNUM$, COLITEM$, COLCAT$, COLQTY$, COLTYPE$
            InputStr$ = ""
            InputStr$ = InputStr$ + CHR$(34) + COLDATE$ + CHR$(34) + ","
            InputStr$ = InputStr$ + CHR$(34) + COLTIME$ + CHR$(34) + ","
            InputStr$ = InputStr$ + CHR$(34) + COLNUM$ + CHR$(34) + ","
            InputStr$ = InputStr$ + CHR$(34) + COLITEM$ + CHR$(34) + ","
            InputStr$ = InputStr$ + CHR$(34) + COLCAT$ + CHR$(34) + ","
            InputStr$ = InputStr$ + CHR$(34) + COLQTY$ + CHR$(34) + ","
            InputStr$ = InputStr$ + CHR$(34) + COLTYPE$ + CHR$(34) + ","
            InputStr$ = InputStr$ + CHR$(34) + outlets(i) + CHR$(34)
            PRINT #1, InputStr$
        LOOP
        CLOSE #2
    END IF
NEXT


Run this! This time, use "20221012" and "filestitch" as the inputs, to fit the file names.




And it's done.




A new file, tx_20221210_consolidated.csv, should appear in the directory. Its contents should be the combination of all the other files that have "20221210" in the filename. And at the end of each line, we have appended the outlet name!
...
"2022-12-10","15:21:02","100001210027","Strawberry Deluxe Surprise","MAIN","1","TAKE-AWAY","TUAS"
"2022-12-10","16:16:08","100001210028","Mixed Fruit Cheesecake","SIDE","1","TAKE-AWAY","TUAS"
"2022-12-10","17:09:14","100001210029","Mixed Fruit Cheesecake","SIDE","1","TAKE-AWAY","TUAS"
"2022-12-10","18:55:55","100001210030","Blueberry Muffin","SIDE","12","TAKE-AWAY","TUAS"
"2022-12-10","19:21:41","100001210031","Over-the-top Meltdown","MAIN","1","TAKE-AWAY","TUAS"
"2022-12-10","19:44:30","100001210032","Banana Sandwich Special","MAIN","10","TAKE-AWAY","TUAS"
"2022-12-10","19:51:02","100001210033","Strawberry Deluxe Surprise","MAIN","5","TAKE-AWAY","TUAS"
"2022-12-10","19:51:02","100001210033","Blueberry Muffin","SIDE","5","TAKE-AWAY","TUAS"
"2022-12-10","19:52:11","100001210034","Chocolate Cat And Mouse","MAIN","1","TAKE-AWAY","TUAS"
"2022-12-10","19:53:41","100001210035","Over-the-top Meltdown","MAIN","1","DINE-IN","TUAS"
"2022-12-10","19:53:41","100001210035","Chocolate Overload","MAIN","1","DINE-IN","TUAS"
"2022-12-10","19:55:58","100001210036","Cream Cake","SIDE","4","TAKE-AWAY","TUAS"
"2022-12-10","19:56:55","100001210037","Fruit Fritters","SIDE","10","TAKE-AWAY","TUAS"
"2022-12-10","19:58:10","100001210038","Strawberry Deluxe Surprise","MAIN","1","DINE-IN","TUAS"
"2022-12-10","19:58:10","100001210038","Hot Six","MAIN","1","DINE-IN","TUAS"
"2022-12-10","19:58:10","100001210038","Blueberry Muffin","SIDE","5","DINE-IN","TUAS"
"2022-12-10","19:58:10","100001210038","Cream Cake","SIDE","2","DINE-IN","TUAS"
"2022-12-10","19:58:10","100001210038","Ice Lychee Tea","DRINK","10","DINE-IN","TUAS"
"2022-12-10","19:58:10","100001210038","Ice Lemon Tea","DRINK","2","DINE-IN","TUAS"
"2022-12-10","10:45:36","444002770055","Mixed Fruit Cheesecake","SIDE","1","DINE-IN","PASIR RIS"
"2022-12-10","10:45:36","444002770055","Hot Six","MAIN","1","DINE-IN","PASIR RIS"
"2022-12-10","10:45:36","444002770055","Chocolate Overload","MAIN","1","DINE-IN","PASIR RIS"
"2022-12-10","10:58:09","444002770056","Over-the-top Meltdown","MAIN","2","TAKE-AWAY","PASIR RIS"
"2022-12-10","10:58:09","444002770056","Passionfruit Excitement","MAIN","2","TAKE-AWAY","PASIR RIS"
"2022-12-10","10:58:09","444002770056","Mixed Fruit Cheesecake","SIDE","6","TAKE-AWAY","PASIR RIS"
...


Happy programming!

It's not often I get to use QBasic professionally, much less write an entire web tutorial for it. I thoroughly enjoyed myself here.

By fair means or file,
T___T

Monday, 6 March 2023

What we think we deserve, does not matter

There is one word that has been bothering me in recent years. It is the word "deserve". It is usually used in the context of what people should have, as opposed to what they do have.

And that is problematic.

What people think they deserve, varies from person to person. Do you think you deserve to be paid more than you are currently? Perhaps, but your employer is almost certainly going to disagree. Who's right? Who has the authority to decide? Is there an objective measure to determine exactly what each person deserves?

Short answer: no, there isn't. The world is not fair, nor was it ever supposed to be. Put on your big boy pants and deal with it.

You may be legally entitled to something. That's an entirely different kettle of fish from saying you deserve something. Saying "x deserves y" carries connotations of morality, and as we all know, morality is subjective. Whose moral scale do we use? Mine? Yours? The Dalai Lama's? The Pope's? We have to define those things before any of these "deserving" statements would even begin to make sense.

Who decides what
you deserve?


So, who gets to judge?

Examples

Another problem I have with the word "deserve" being used, is that there's almost always an element of emotionality involved.

"I deserve better because I'm a good person."


This is a useless statement. What's "better"? What's "good"? None of these are measurable. And thus, no steps are actionable.

I've been good.

Also, what does being "good" have anything to do with deserving something? Saying you deserve something implies that it should be handed to you without any action on your part. And that is a denial of reality.

"Tell me why you deserve to get paid this much."


This question sometimes appears during job interviews, and has the effect of making me eye-roll really hard.

Oh FFS. If you just want me to sell myself to you, just come right out and say it. Fact is, no one "deserves" a high salary, and to claim otherwise is ridiculous. How much employers pay me is pretty much dependent on what they can afford to pay, what they are willing to pay and (let's just be real here) what they can get away with without getting in trouble with the law. The rest is just fluff.

"You deserve to be loved for who you are."


Self-proclaimed life coaches and their ilk seem to use this constantly. It's a mollycoddling refrain that says it doesn't matter how flawed your character is, you deserve to be loved like everyone else. Nobody died and made me God, so I'm not going to judge that statement. But I am absolutely going to judge the fuck out of what that statement implies.

This statement, intentionally or otherwise, implies that you are perfectly flawed just like everyone else, and discourages people from working on themselves. Why would they? If I decide that I am the way I am and anyone who chooses me is just going to have to live with it, what's there to work on? Sure, I still deserve to be loved... but this severely limits my options. That is the reality.

"People deserve to live, too."


This is usually some self-righteous rubbish from people who need some kind of external validation from the people listening to them. I really don't have time for that.

I usually hear this in the context of the ongoing Russia-Ukraine war, and that level of moral posturing is nauseating. Should people have to suffer the hardship of war? No one is arguing that they should. It is, however, an unfortunate fact that there is a war going on at this moment and that statistically, there are casualties in war. Saying that "people deserve to live, too" is useless self-serving rhetoric that does nothing for anyone. Like, does anyone on this planet not understand that people deserve to live? How is this helpful?

Actions instead of words

Life is not about what you deserve and don't deserve. It's about what you can and can't get. Anyone who tells you different, is probably trying to sell you something.

What people eventually get, tends to be a function of what they can get as opposed to what they deserve. Focusing on what is possible is more productive than focusing on what is desired.

Foreign software developers
in Singapore.

Are you in an environment where your desired outcomes aren't possible? Go elsewhere. Why else do you think foreign software developers ply their trade in Singapore? Because they love being shit on by locals?

Is there a surfeit of people who would love what you are? Look in the proverbial mirror and try to work your issues out. The onus is on you.

Do your skills have market value? If not, get new skills. For example, I could be a QBasic guru, but if there's a limited market for antiquated programming language, I'm not going to get very far. It is what it is.

Concerned about war? Maybe send some money to Ukraine (or Russia, if that's how you wanna roll) and spend less time grandstanding on Social Media. The money is infinitely more practical than your thoughts and prayers.

Last words and just desserts

The question is not "do I deserve this?"; rather, the question should be "can I get this, and how?" Once we fix that first step, the rest naturally falls into place.

Deservedly,
T___T

Saturday, 3 September 2022

The Good Old Variable Swap, Redux

Some time back, I wrote a short piece, a how-to on the classic variable swap. Today is a good time to revisit that. As with the last time, code samples are in QBasic.

I was demonstrating this in a class, and one of the students had this bright idea: would this method work with multiplication and division, instead of addition and subtraction?

His idea was as follows.
DIM a = 3
DIM b = 2

a = a * b
b = a / b
a = a / b


And my answer was an unequivocal no. This is why.

In his example, a was 3 and b was 2. That would work. But consider this: what if either a or b was 0? Let's try both times.

Example 1: a = 0, b = 2
DIM a = 0
DIM b = 2

'a = 0 * 2 = 0
a = a * b

'b = 0 / 2 = 0
b = a / b

'a = 0 / 0
a = a / b


Example 2: a = 3, b = 0
DIM a = 3
DIM b = 0

'a = 3 * 0 = 0
a = a * b

'b = 0 / 0
b = a / b

a = a / b


In either case, at some point we are going to end up in a scenario where a division by zero would be attempted. And as we all know, once a division by zero is attempted, an exception occurs.

The dreaded zero.

We could get around this limitation by doing a Try-catch, and assuming the value of 0 once the exception is thrown. But that would add a level of complexity to what is supposed to be a very simple formula.

The takeaway

It was a nice try, and I like to encourage that. An experimental and curious mindset is always valuable to a programmer. What's important is that we always bear certain basics in mind.

Happy pr0gramming,
T___T

Tuesday, 11 January 2022

Functional Terminology

Some terms in programming tend to confuse new programmers. There's a certain historical context to them, and with the rise of more recent technology, new terms muddy the waters a bit.

And among these terms are: function, subroutine and method. They all seem to be referring to the same thing - procedures - but there are some significant differences.

Today I will be attempting to provide some clarity.

Subprocedures

These are procedures that exist in a block of code, and can be called from anywhere. Arguments could be passed into them, optionally. In QBasic back in the day, there was a specific keyword for that.

SUB HELLOWORLD()
    PRINT "HELLO WORLD"
END SUB


CALL HELLOWORLD()


Basically, the subprocedure would perform whatever operations the programmer put in it, and that would be the end of it. This has all but vanished from the programming landscape. These days, it's all about functions, which we will examine below.

Functions

Functions are also procedures that exist in a block of code, and can be called from anywhere. Arguments could be passed into them, optionally. The difference is that after performing the operations, they return a value.

This is how it was done in QBasic or Visual Basic.

FUNCTION HELLOWOWLD(str)
    HELLOWORLD = str
END FUNCTION


PRINT HELLOWORLD("Hello world")


Or in JavaScript...
function helloWorld(str)
{
    return str;
}


console.log(helloWorld("Hello world"));


...and in certain cases may not even return anything.
function helloWorld(str)
{
    console.log(str);
}


helloWorld("Hello world");


So in these cases, a function works just like a subroutine, and thus subroutines are not really needed any more.

Methods

Now we come to methods. Methods are just like functions in every way... except for access control.

We call a function a method when it's part of an object. This is an example in JavaScript.
let obj =
{
    helloWorld: function (str)
    {
        console.log(str);
    }
}


So if we were to call this method, we can only call it when specifying the parent object.
obj.helloWorld("Hello world");


If we were to treat it like any other function, it would not work.
helloWorld("Hello world");


In some object-oriented programming languages such as Java or C++, if you specify that the class is Private (for instance), the method can only be called within the class itself. There's actually a fair bit to cover on this subject, but it's out of scope for this blogpost.

Finally()

As long as you have a clear idea of what you are referring to when you use these terms, end of the day it doesn't really matter what terms you use. Communication is key. Just don't be confused when the context changes, and the terms change with it. Ultimately, it's generally the same thing.

Play that func-y music!
T___T

Saturday, 3 October 2020

TeochewThunder: Year Six (Part 1/2)

2020 has been a turbulent year, not just for me, but for just about everyone on the planet. It will forever be known as the year COVID-19 ravaged the world, and the scary thing is, it's far from over. At least not if USA and India have anything to say about it. As befits such a world-shattering event, many of my blogposts this year revolved around COVID-19 as a subject. Not only did this unprecedented crisis give rise to much technical innovation, it also forced changes in the way many people do things, thus forcing even more innovation. The effects were far-reaching and felt even to this day.

Somewhat closer to home, the Singapore General Elections were held around this time. This also gave rise to a series of writings that sometimes coincided with views on COVID-19. This wasn't done on purpose, I swear. COVID-19 just got its hooks into everything.

As it stands, I've just lost my job last month. Long story. As of the first day of this month, I landed another job (though goodness knows how long that will last) and I'm scheduled for yet another stint in school starting this month. The yearly blogging break could not have come at a better time.

Here are some notes about content on this blog...

Web Tutorials

The web tutorials have mostly focused around a whole lot of JavaScript. What can I say; I'm a severely limited programmer and JavaScript happens to be an old friend. There's been new stuff, obviously - D3.js, ReactJS... yes, it's all still JavaScript, but it's different ways of applying the JavaScript ecosystem. You can be a good or bad programmer, but above all, have fun.

My love for learning new stuff has never been put to the test quite as severely as the past year. Everything I learned challenged my assumptions and showed me I know a lot less than I thought I did, and that I have a long way to go before considering myself even mildly accomplished. Which is fine by me; ego is overrated.

A long, long climb.

Despite writing about Ruby just two months ago, I've not actually had a chance to touch it much. It's the same with QBasic. This year has been almost exclusively JavaScript and PHP. All in all, my grasp on tech has evolved. Though, it has to be said, I'm still trying to keep the web tutorials at levels that are accessible to beginners such as myself back then. In many ways, I'm still that wide-eyed kid playing with stuff.

Reviews

Thankfully, unlike last year, I managed to tone down on the overwhelming number of reviews, and actually varied the kind of reviews on offer. There's a recent Reference Review I did on The Mythical Man-month: Essays on Software Engineering, for instance.

No time to play.


I've not had much time or inclination to play or review mobile games, which is a real pity. The mobile game makers seem to be getting more innovative by the day. I guess they have to, with all the competition that's out there, and the notoriously short attention span of their target audience.

Rants

As mentioned in the very first paragraph, there's been quite a few rants on this blog involving either the Singapore General Elections or COVID-19, or both. Regrettably, I've used more vulgarities than was absolutely necessary, and while I don't apologize for it, I acknowledge that things could have been worded a lot less offensively. Could.


Having things to say.


All in all, though, I've been way less of a windbag this year than I've been prone to being the past few years, even though COVID-19 has given me plenty to rant about. Maybe it's because I got most of it out of my system. No point repeating myself.

Or possibly, I'm just getting old.

Others

As mentioned, there's been considerably more diversity in feature blogposts - a good balance of reviews, listicles and what-not. I will strive to maintain that balance.

That tends to be hard because daily, there is tech-related breaking news and I need to decide if I care about it enough to include it in TeochewThunder. And if I do, I need to make hard decisions about what to post now, and what to post later.

Next

Examining what resonated with readers, and what didn't.

Sunday, 30 August 2020

How I Learned Ruby

It was a fine weekend in 2017 when I started learning Ruby. Three years have passed and I wouldn't exactly call myself an expert... but I have reached a certain level of proficiency in it. What's remarkable is that the way I learned it didn't really follow the way I learned ASP back in the 2000s or PHP in 2010. Back then, I picked up a book, started reading, and then started doing. How I learned Ruby was a little less straightforward than simply reading a book.

Why did I pick up Ruby?

You know what, that's an excellent question. I don't know exactly. I was jobless, low on confidence, and needed to distract myself by learning something. Something or other brought me to this site, Try Ruby. I went through their online tutorial and the Ruby syntax was pretty straightforward. It just kind of flowed. It was only a while later that I read a description of Ruby as a programmer's best friend, and by that time I thoroughly agreed.

What the hell, right? It wasn't like I had anything else to do between attending interviews and waiting for replies to my job applications. So I rolled up my metaphorical sleeves and got cracking.

The process

First was the installation of the Ruby server on my aging laptop. It turned out to be a breeze, especially when you consider all the pain I experienced while setting up the WAMP stack for PHP back then. And then in the Command Line Interface, I learned a bit by typing a few commands here and there.

Then I started typing in entire block codes into text files and running them on the CLI. Now, this was something I could get into. After all, I'm doing pretty much the same thing whenever I use QBasic.

That was when I started doing katas. It was easy stuff like the FizzBuzz algorithm, simple calculators, and so on. Stuff that used basic code blocks like inputs, outputs, If-Else, For... you know, the usual suspects. I even picked up this book, Exercises for programmers: 57 challenges to develop your coding skills by Brian P. Hogan, and did quite a few exercises in them using Ruby. One of them resulted in the Ruby Website Template Generator.


And speaking of books, I did eventually get around to reading them, just to see if it could help reinforce everything I thought I'd learned so far. Ruby recipes: a problem-solution approach by Malay Mandal was one of them.


What I did learn was that there was a lot more detail to the Ruby commands and functions that I had been using - stuff I never knew about simply because my use cases never touched them. Interesting. Perhaps not absolutely necessary, but good to know.

I was starting to write Ruby programs that were still rudimentary, but had more moving parts. By this time, I had landed a job at a major tech company that used a lot of C#. Ruby was a non-essential part of my career at this point, but I kept using it now and then in my off-time because it was just such a joy to code in.

Ruby on Rails

But learning Ruby on its own just doesn't cut it these days. If I wanted to get some real value out of that experience, I was going to have to graduate to using its most famous framework - Ruby On Rails. This was done by exploration of the Rails for Zombies portal, which gave me a fun little kick start. I love programming, and I love zombies.


Turns out Ruby On Rails wasn't such a big revelation as I thought it would be. I picked up some more reading from the local library. All things considered, it was pretty dry.


The Rails view: creating a beautiful and maintainable user experience by John Athayde and Bruce Williams


Rails crash course: a no-nonsense guide to Rails development by Anthony Lewis

Editor's Note: I am neither recommending nor not recommending these books. They were well-edited; I just didn't get a whole lot of value out of them and would have been fine not having read them. But that doesn't mean you're going to have the same experience.

After starting up a Rails server and going through all that stuff about Models, Controllers, Views and Helpers, I quickly realized that this was pretty much the same shit I'd gone through with ASP.NET 4.x or even CodeIgniter. I mean, there just doesn't seem to be that many ways to implement MVC, does it?

To test out this theory, I had to build a website. There was this thing my ex-boss implemented - a multi-lingual site that allowed the user to switch languages at will. I had an itch to try it for myself. To make the exercise even more interesting, I implemented the idea in Ruby On Rails.

And it really wasn't that hard. Personally, I feel that if you're having a problem with Ruby On Rails, your problem might be Ruby itself, or the fact that you need to understand MVC. If you've done both, this is a piece of cake.

Epilogue

Years after the fact, I'm still using Ruby. It's great for recreational programming and I'm getting as much use out of it as QBasic, which is a pity because it can be used for so much more. I got into it because I was idle at the time and needed to engage in a nominally productive pursuit. Perhaps that's why I never delved into it the same way I did for PHP - because my professional survival wasn't on the line.

Also, I discovered something strange. While the Ruby syntax was really a delight to use - terse, uncomplicated and smooth as silk - I had trouble memorizing it the way I did for JavaScript and QBasic back then. Maybe my memory's just not what it used to be, or maybe I've just evolved as a programmer. I'm no longer interested in memorizing commands - I want to get stuff done, and I have no compunction against copying and pasting from the internet, and modifying stuff to make it work. And perhaps, psychologically, since there's no longer any real motivation to memorize anything, I simply don't.

But really, if you're looking to pick up an extra language, I couldn't recommend Ruby enough.

Try it. It's a real gem!
T___T