Tuesday, August 31, 2010

The First Free Hydroponic Nutrient Calculator Program :o)

After a lot of work and effort today I have the pleasure to share with you my latest development in the area of hydroponic nutrient design and creation, a totally new and free hydroponic nutrient calculator which I programmed using Delphi. This piece of software is absolutely unique and I can guarantee that you will not find any other similar program on the internet. The piece of code I am about to release was the natural evolution of my excel spreadsheet (which comes with my free ebook) and now has a ton more features which should make hydroponic nutrient calculation for absolutely anyone a total breeze. On today's post I want to share with you this program as well as a general description of its scope, uses and improvements over the excel spreadsheet available for nutrient calculation within my ebook.

One of my main objectives has always been to design and prepare my own hydroponic nutrien! t solutions and to help others do the same thing. Preparing your own solutions is not only something that gives you absolute control over the composition of the nutrients you are giving to your plants but it also allows you to make absolutely HUGE saving on commercial fertilizers. Using commercial liquid concentrated solutions the cost of a hydroponic garden is usually above 0.20 USD per gallon while if you make your own nutrients this price can drop as low as 0.01 USD per gallon. Additionally people from less developed countries where hydroponics are not very well established will be able to greatly benefit from making their own blends with this sometimes being the only way to have a hydroponic crop.
-

-
The program I am releasing today will allow you to calculate the amount of salts you need to weight to arrive to a given hydroponic formula. The features of the program and its advantages over the spreadsheet previously available are highlighted below :
  • Ability to calculate weights of salts for any given hydroponic formulation
  • No need to have excel :o) any Windows platform will do.
  • Flexibility ! Choose which salts you want to use to makeup your nutrient solution
  • The program automatically determines which salts are better than others or pairs them up adequately if you have sever! al sources for the same nutrient
  • Input the volume you want in Liters, gallons or cubic meters
  • The program automatically determines if you need or don't need to prepare concentrated micro-nutrient and Fe solutions depending on the size of your reservoir
  • You can also choose to add all salts directly if you have scales with the necessary precision
  • Concentrated A+B solutions can be prepared
  • Save your solutions ! You can easily save your recipes to a text file for later printing or reference
  • Save salt selection
  • Select water quality parameters
  • Automatically corrects the weights of salts against your water quality analysis
  • Example recipe obtained using the program and the list of salts I personally use (although you may change those salts for others depending on where you live)
  • Automatically check for new updates
  • And many, many more ! :o)!
As yo! u see, t his new program is a great leap forward in custom nutrient solution design and preparation for the average hydroponic hobbyist and small business owner. Hopefully with this program you will be able to prepare your own solutions or improve your previous calculations if you had been using the spreadsheet. I hope that you enjoy the new program and leave any feedback you have here. As always I will be glad to take into account and implement your requests :o)
-

addition method calculator

1895 8th Grade Final Exam

Dear Readers,
Roughly, how many of the following questions do you think you can answer? For me, definitely not enough to warrant my 17 years of public education. The format, depth, and range of these questions would make good guidelines for homeschooling. The "Rules for Teachers" that follows is also quite interesting. I will be curious to hear your thoughts.

The following document was transcribed from the original document in the collection of the Smoky Valley Genealogy Society, Salina, Kansas. This test is the original eighth-grade final exam for 1895 from Salina, KS. An interesting note is the fact that the county students taking this test were allowed to take the test in the 7th grade, and if they did not pass the test at that time, they were allowed to re-take it again in the 8th grade.


Reading and Penmanship. - The Examination will be oral, and the Penmanship of Applicants will be graded from the manuscripts.
************************
GRAMMAR
(Time, one hour)
1. Give nine rules for the use of Capital Letters.
2. Name the Parts of Speech and define those that have no modifications.
3. Define Verse, Stanza and Paragraph.
4. What are the Principal Parts of a verb? Give Principal Parts of do, lie, lay and run.
5. Define Case. Illustrate each case.
6. What is Punctuation? Give rules for principal marks of Punctuation.
7-10 Write a composition of about 150 words and show therein that you understand the practical use of the rules of grammar.
************************
ARITHMETIC
(Time, 1 ¼ hour)

1. Name and define the Fundamental Rules of Arithmetic.
2. A wagon box is 2 ft. deep, 10 feet long, and 3 ft. wide. How many bushels of wheat will it hold?
3. If a load of wheat weights 3942 lbs., what is it worth at 50 cts. Per bu., deducting 1050 lbs for tare?
4. District No. 33 has a valuation of $35,000. What is the necessary levy to carry on a school seven months at $50 per month, and have $104 for incidentals?
5. Find cost of 6720 lbs. coal at $6.00 per ton.
6. Find the interest of $512.60 for 8 months and 18 days at 7 per cent.
7. What is the cost of 40 boards 12 inches wide and 16 ft. long at $20 per m?
8. Find bank discount on $300 for 90 days (no grace) at 10 per cent.
9. What is the cost of a square farm at $15 per acre, the distance around which is 640 rods?
10. Write a Bank Check, a Promissory Note, and a Receipt.
*************************************
U.S. HISTORY
(Time, 45 minutes)
1. Give the epochs into which U.S. History is divided.
2. Give an account of the discovery of America by Columbus.
3. Relate the causes and results of the Revolutionary War.
4. Show the territorial growth of the United States.
5. Tell what you can of the history of Kansas.
6. Describe three of the most prominent battles of the Rebellion.
7. Who were the following: Morse, Whtney, Fulton, Bell, Lincoln, Penn, and Howe?
8. Name events connected with the following dates: 1607, 1620, 1800, 1849, and 1865.
*******************************************
ORTHOGRAPHY
(Time, one hour)
1. What is meant by the following: Alphabet, phonetic orthogaphy, etymology, syllabication?
2. What are elementary sounds? How classified?
3. What are the following, and give examples of each: Trigraph, subvocals, diphthong, cognate letters, linguals?
4. Give four substitutes for caret ãuä.
5. Give two rules for spelling words with final ãeä. Name two exceptions under each rule.
6. Give two uses of silent letters in spelling. Illustrate each.
7. Define the following prefixes and use in connection with a word: Bi, dis, mis, pre, semi, post, non, inter, mono, super.
8. Mark diacritically and divide into syllables the following, and name the sign that indicates the sound: Card, ball, mercy, sir, odd, cell, rise, blood, fare, last.
9. Use the following correctly in sentences: Cite, site, sight, fane, fain, feign, vane, vain, vein, raze, raise, rays.
10. Write 10 words frequently mispronounced and indicate pronunciation by use of diacritical marks and by syllabication.
*****************************************
GEOGRAPHY
(Time, one hour)
1. What is climate? Upon what does climate depend?
2. How do you account for the extremes of climate in Kansas?
3. Of what use are rivers? Of what use is the ocean?
4. Describe the mountains of N.A.
5. Name and describe the following: Monrovia, Odessa, Denver, Manitoba, Hecla, Yukon, St. Helena, Juan Fernandez, Aspinwall, and Orinoco.
6. Name and locate the principal trade centers of the U.S.
7. Name all the republics of Europe and give capital of each.
8. Why is the Atlantic Coast colder than the Pacific in the same latitude?
9. Describe the process by which the water of the ocean returns to the sources of rivers.
10. Describe the movements of the earth. Give inclination of the earth.

1. Where are the saliva, gastric juice, and bile secreted? What is the use of each in digestion?
2. How does nutrition reach the circulation?
3. What is the function of the liver? Of the kidneys?
4. How would you stop the flow of blood from an artery in the case of laceration?
5. Give some general directions that you think would be beneficial to preserve the human body in a state of health.
*********************************************

RULES FOR TEACHERS
1872

1. Teachers each day will fill lamps, clean chimneys.
2. Each teacher will bring a bucket of water and a scuttle of coal for the dayâs session.
3. Make your pens carefully. You may whittle nibs to the individual taste of the pupils.
4. Men teachers may take one evening each week for courting purposes, or two evenings a week if they go to church regularly.
5. After ten hours in school, the teachers may spend the remaining time reading the Bible or other good books.
6. Women teachers who marry or engage in unseemly conduct will be dismissed.
7. Every teacher should lay aside from each pay a goodly sum of his earnings for his benefit during his declining years so that he will not become a burden on society.
8. Any teacher who smokes, uses liquor in any form, frequents pool or public halls, or gets shaved in a barber shop will give good reason to suspect his worth, intention, integrity and honesty.
9. The teacher who performs his labor faithfully and without fault for five years will be given an increase of twenty-five cents per week in his pay, providing the Board of Education approves
*********************************************

8th grade math final exam

Formula for Converting Dollar Amount to Words

The article on Microsoft.com "How to convert a numeric value into English words in Excel" provides some VBA code for converting a dollar value into words, like you would see on checks or receipts.

I try to avoid using VBA in my spreadsheets whenever possible, and sometimes that leads to some very very long formulas. This one took some doing, but it IS possible to use a formula to convert dollar amounts less than 1 million into words without VBA. I wasn't able to extend this to numbers larger than a million because the formula exceeded the maximum length.

I use the formula in my new Receipt Template spreadsheet, where I highly doubt anyone would need to write a receipt for a value over a million.

FIRST, you need to define two named ranges:




THEN, you can use one of the following formulas, where cell B8 contains the numeric value. Important: The value in B8 must already be rounded to the nearest cent (0.01). A value like 999.995 will mess up the formula.

Style 1: ... and ../100


Example: B8=123,456.78
Result: "One Hundred Twenty-Three Thousand Four Hundred Fifty-Six and 78/100"

Style 2: ... Dollars and ... Cents



Example: B8=123,456.78
Result: "One Hundred Twenty-Three Thousand Four Hundred Fifty-Six Dollars and Seventy-Eight Cents"

You could create a formula in Excel that would handle much larger numbers if you split the formula into multiple cel! ls. I suspect that the formula could be shortened further with! some mo re effort, but I just don't care enough to do that.

y intercept formula

The Equation to Surviving Andersonville (Hint: Pr (S=1) = Φ(Ã(1)F + Ã(2)I)

A recent scientific article attracted my attention:

Costa DL and Kahn ME, "Health, wartime stress, and unit cohesion: evidence from Union Army veterans," Demography, Feb 2010 Feb, Vol. 47, No. 1, pp. 45-66 (abstract here; PDF here)

I contacted Dr. Dora Costa at UCLA f! or a copy of the article (which she kindly sent) and I am reading it now.

She also sent along another article which is especially interesting:

Costa DL and Kahn ME, "Surviving Andersonville: The Benefits of Social Networks in POW Camps," American Economic Review, 2007, 97(4): 1467-1487. (PDF here)

Readers are (gently) warned that the papers are heavy in mathematics and statistics, but the authors' arguments are fascinating, especially for the Andersonville paper. While there is obviously no true "equation" to explain survival, the heavy "ciphering" in the papers goes with the rigors of social science. Yet, the introduction, discussion of results, and conclusion of both papers are very readable.

As for the Andersonville paper: briefly, they conclude tha! t being part of a "social network" (I don't mean Facebook...se! e here for a scientific definition) was a key factor in surviving the prison by analyzing a mountain of data from Union army service and pension records to identify variables that had a statistically significant impact on survival. It turns out, according to Drs. Costa and Kahn, that "In two independent datasets we found that friends had a statistically significant positive effect on survival probabilities, and that the closer the ties between friends as measured by such identifiers as ethnicity, kinship, and the same hometown, the bigger the effect."

The article crosses a number of economic and social science disciplines including aging studies, game theory, and others.

Look for an interview with Dr. Costa about her interesting research and conclusions in my "Medical Department" column in The Civil War News in the coming months!

writing an equation

How to Gather Manually Statistics

Oracle: 10gr1
OS: Windows 32bit


Statistics Gathering Using Sampling




The statistics-gathering operations can utilize sampling to estimate statistics. Sampling is an important technique for gathering statistics. Gathering statistics without sampling requires full table scans and sorts of entire tables. Sampling minimizes the resources necessary to gather statistics.

Sampling is specified using the ESTIMATE_PERCENT argument to the DBMS_STATS procedures. Oracle Corporation recommends setting the ESTIMATE_PERCENT parameter of the DBMS_STATS gathering procedures to DBMS_STATS.AUTO_SAMPLE_SIZE to maximize performance gains while achieving necessary statistical accuracy. AUTO_SAMPLE_SIZE lets Oracle determine the best sample size necessary for good statistics, based on the statistical property of the object. Because each type of statistics has different requirements, the size of the actual ! sample taken may not be the same across the table, columns, or indexes.

EXECUTE DBMS_STATS.GATHER_SCHEMA_STATS('OE',DBMS_STATS.AUTO_SAMPLE_SIZE);



Parallel Statistics Gathering




The statistics-gathering operations can run either serially or in parallel. The degree of parallelism can be specified with the DEGREE argument to the DBMS_STATS gathering procedures. Parallel statistics gathering can be used in conjunction with sampling. Oracle Corporation recommends setting the DEGREE parameter to DBMS_STATS.AUTO_DEGREE. This setting allows Oracle to choose an appropriate degree of parallelism based on the size of the object and the settings for the parallel-related init.ora parameters.

Note that certain types of index statistics are not gathered in parallel, including cluster indexes, domain indexes, and bitmap join indexes.
Statistics on Partitioned Objects

For partitioned tables and indexes, DBMS_STATS can gath! er separ ate statistics for each partition, as well as global statistics for the entire table or index. Similarly, for composite partitioning, DBMS_STATS can gather separate statistics for subpartitions, partitions, and the entire table or index. The type of partitioning statistics to be gathered is specified in the GRANULARITY argument to the DBMS_STATS gathering procedures.

Depending on the SQL statement being optimized, the optimizer can choose to use either the partition (or sub partition) statistics or the global statistics. Both types of statistics are important for most applications, and Oracle Corporation recommends setting the GRANULARITY parameter to AUTO to gather both types of partition statistics.


Column Statistics and Histograms




When gathering statistics on a table, DBMS_STATS gathers information about the data distribution of the columns within the table. The most basic i! nformation about the data distribution is the maximum value and minimum value of the column. However, this level of statistics may be insufficient for the optimizer's needs if the data within the column is skewed. For skewed data distributions, histograms can also be created as part of the column statistics to describe the data distribution of a given column.

Histograms are specified using the METHOD_OPT argument of the DBMS_STATS gathering procedures. Oracle Corporation recommends setting the METHOD_OPT to FOR ALL COLUMNS SIZE AUTO. With this setting, Oracle automatically determines which columns require histograms and the number of buckets (size) of each histogram. You can also manually specify which columns should have histograms and the size of each histogram.




Determining Stale Statistics




Statistics must be regularly gathered on database objects as those databa! se objects are modified over time. In order to determine wheth! er or no t a given database object needs new database statistics, Oracle provides a table monitoring facility. This monitoring is enabled by default when STATISTICS_LEVEL is set to TYPICAL or ALL. Monitoring tracks the approximate number of INSERTs, UPDATEs, and DELETEs for that table, as well as whether the table has been truncated, since the last time statistics were gathered. The information about changes of tables can be viewed in the USER_TAB_MODIFICATIONS view. Following a data-modification, there may be a few minutes delay while Oracle propagates the information to this view. Use the DBMS_STATS.FLUSH_DATABASE_MONITORING_INFO procedure to immediately reflect the outstanding monitored information kept in the memory.

The GATHER_DATABASE_STATS or GATHER_SCHEMA_STATS procedures gather new statistics for tables with stale statistics when the OPTIONS parameter is set to GATHER STALE or GATHER AUTO. If a monitored table has been modified more than 10%, then these statistics! are considered stale and gathered again.

BEGIN
SYS.DBMS_STATS.GATHER_SCHEMA_STATS (
OwnName => 'SCOTT'
,Granularity => 'ALL'
,Options => 'GATHER'
,Gather_Temp => TRUE
,Estimate_Percent => SYS.DBMS_STATS.AUTO_SAMPLE_SIZE
,Method_Opt => 'FOR ALL COLUMNS SIZE AUTO '
,DEGREE => 8
,CASCADE => TRUE
,No_Invalidate => FALSE);
END;

PL/SQL procedure successfully completed.
------------------------------------------
begin
dbms_stats.gather_schema_stats(
ownname => 'SCOTT',
estimate_percent => dbms_stats.auto_sample_size,
degree => dbms_stats.auto_degree,
cascade => TRUE
);
end;
/

PL/SQL procedure successfully completed.



types of histograms

Python String Calculator Kata - Step by Step

Background

For the past week, I have been working on the String Calculator Kata in Python. I watched Gary Bernhardt's version of it and then set out to tackle it myself. Not surprisingly, my solution was much like Gary's.

Of course, watching a master was a bit of a cheat. I hadn't really figured it out. I just saw what he did and mimicked it. I certainly understood what he was doing and I understood the solution I subsequently developed, but I skipped a very important part of the process; the discovery and real learning.

My wife has taken an interest in software development and is considering a possible career change. Along with reading books like "Apprenticeship Patterns", "The Inmates are Running The Asylum", and "Hackers and Painters", she is learning Python.

I decided that the String Calculator Kata would be a good exercise for us to pair on. I could show her some tricks and help her along. This would be a great learning exercise for the both of us.

Kata

Take One
Our first attempt was over the phone with a screen share. I did all the talking and typing while she watched, listened, and provided me with the occasional, "uh-huh" to let me know she was still there. All in all, I'd say it was a fair experience at best. In my opinion, I had shown her quite a bit, but had actually taught her nothing. She wasn't going to benefit from doing a Kata by rote. She needed to understand how the moves came to be and why they were being done.

This was going to take more time.
Take Two
Our second attempt was in person, side by side, sharing a single computer. I still did most of the talking and all of the typing, but the dynamic was very different. Jennifer was able to ask questions, point at the screen, and interact unlike with the screen share. And I took a much more deliberate approach to the Kata. Making no assumptions about the language; acting as if I were learning it all for the first time as well.

This was a good learning experience for both of us. I learned new things about Python and I felt connected to the developer I once was; the guy who would explore a language for the best way to do something; the guy who would spend hours on the same few lines of code in an effort to get them perfect. I've been doing Kata on and off for about a year with a recent deliberateness. But it wasn't until I used it to teach someone else that I achieved the next level of enlightenment.

After some more thought, I decided to recount the entire experience on my blog. I've already discussed Kata in a couple of posts. The string calculator can be found in Scheme and Python on my github. But none of these capture my thought process. I am not saying my thought process is right. Hell, I have no idea. But I think sharing it and soliciting feedback can't hurt.
The full experience
We start, of course, with a single test.
calc_test.py
import nose.tools from calc import add  def test_empty_string_is_0():     assert 0 == add('') 

This fails as expected. And we write our add method.
calc.py
def add(input_string):     return 0 

We add the next test
calc_test.py
import nose.tools from calc import add  def test_empty_string_is_0():     assert 0 == add('')  def test_single_number_returns_value():     assert 1 == add('1')  

Which forces the change:
calc.py
def add(string):     if string:         return 1     else:         return 0 

But this doesn't cover any single number, it covers a particular single number. So let's update the test.
calc_test.py
import nose.tools from calc import add  def test_empty_string_is_0():     assert 0 == add('')  def test_single_number_returns_value():     assert 1 == add('1')     assert 10 == add('10')  
Our code fails again and we need to figure out a way to return the value of a string. Naturally, I asked the googles. They don't know anything themselves, but they always know somebody who knows something.

A tiny bit of poking around, and I found there is a string.atoi(s[, base]) function, but it was deprecated in verison 2.0 in favor of the built-in int() function. This is what we are looking for:
calc.py
def add(string):     if string:         return int(string)     else:         return 0 
Now the test passes, but I want to get cute. I want to eliminate the if/else. I happen to know Python has lambda capability. So I do some research and refactor to:
calc.py
def add(string):     return string and int(string) or 0 
Cute. Yep.

Back to business. We've covered the first couple of criteria, but we now need to handle more than one item. So we write the test (of course).
calc_test.py
import nose.tools from calc import add  def test_empty_string_is_0():     assert 0 == add('')  def test_single_number_returns_value():     assert 1 == add('1')     assert 10 == add('10')  def test_multiple_numbers_returns_sum():     assert 3 == add('1,2') 

The familiar taste of failure leads us in search of an answer. How do we get the values out of a string? As it turns out, str is a sequence type in Python. So we can refer to its elements fairly easily.
calc.py
def add(string):     return string and _sum_string(string) or 0  def _sum_string(string):     return int(string[0]) + int(string[2])  
This gives a string index out of range error on our earlier passing tests. This won't do. We need to refer to the individual values only if there is a comma in the string. Looking back at our reference for sequence types, we find the in operator, which is perfect for this case.
calc.py
def add(string):     return string and _sum_string(string) or 0  def _sum_string(string):     if ',' in string:         return int(string[0]) + int(string[2])     else:         return int(string)  
This works. Let's see how far we can get using lambda expressions (for fun). Refactor to lambda:
calc.py
def add(string):     return string and _sum_string(string) or 0  def _sum_string(string):     return ',' in string and int(string[0]) + int(string[2]) or int(string) 
But what happens when the integers are more than a single digit? This calls for a new test.
calc_test.py
import nose.tools from calc import add  def test_empty_string_is_0():     assert 0 == add('')  def test_single_number_returns_value():     assert 1 == add('1')     assert 10 == add('10')  def test_multiple_numbers_returns_sum():     assert 3 == add('1,2')     assert 15 == add('10,5') 
This fails with a ValueError. It appears to be trying to get the int() value of a comma. So our hard-coded string offsets won't do. Then what else can we do here?

Perusal of the standard string methods, leads us to the built-in partition method that was added in version 2.5. If it was recently added, it must be what we need....
calc.py
def add(string):     return string and _sum_string(string) or 0  def _sum_string(string):     return ',' in string and _sum_delimited_string(string) or int(string)  def _sum_delimited_string(string):     string_parts = string.partition(',')     return int(string_parts[0]) + int(string_parts[2]) 
And the tests pass. But my wife points out this won't work for strings with more than two digits.
Hmmmm....
Let's confirm the assumption.
calc_test.py
import nose.tools from calc import add  def test_empty_string_is_0():     assert 0 == add('')  def test_single_number_returns_value():     assert 1 == add('1')     assert 10 == add('10')  def test_multiple_numbers_returns_sum():     assert 3 == add('1,2')     assert 15 == add('10,5')     assert 25 == add('10,3,12') 
She was right. We get another ValueError. This time we are trying to get the int() of '3,12'. I see an immediate solution that should work. A little recursion and we are good to go.
calc.py
def add(string):     return string and _sum_string(string) or 0  def _sum_string(string):     return ',' in string and _sum_delimited_string(string) or int(string)  def _sum_delimited_string(string):     string_parts = string.partition(',')     return int(string_parts[0]) + _sum_string(string_parts[2]) 
And once again, our tests pass.

At this point, I am actively holding myself back. I don't like this form of recursion. If I'm going to do it, I want it all contained in a single function. I really want to use split(), map(), and sum(), but I must remember that we've not discovered these yet. We are solving the problem with what we currently "know".

Roy's first two requirements down, now we move on to custom delimiters; starting with a new-line as a delimiter.

calc_test.py
import nose.tools from calc import add  def test_empty_string_is_0():     assert 0 == add('')  def test_single_number_returns_value():     assert 1 == add('1')     assert 10 == add('10')  def test_multiple_numbers_returns_sum():     assert 3 == add('1,2')     assert 15 == add('10,5')     assert 25 == add('10,3,12')  def test_newline_is_a_delimiter():     assert 6 == add('1\n2,3') 
Now we get a ValueError on an attempt to get int() for '1\n2'. Let's update the code to get this to pass.
calc.py
def add(string):     return string and _sum_string(string) or 0  def _sum_string(string):     return ',' in string and _sum_delimited_string(string) or int(string)  def _sum_delimited_string(string):     string = _normalize_delimiters(string)     string_parts = string.partition(',')     return int(string_parts[0]) + _sum_string(string_parts[2])  def _normalize_delimiters(string):     return string.replace('\n', ',') 
We are back to green. We are moving on to the fourth requirement; custom delimiters. This requirement reads as follows:
  1. Allow the Add method to handle a different delimiter:
    1. to change a delimiter, the beginning of the string will contain a separate line that looks like this: “//[delimiter]\n[numbers…]” for example “//;\n1;2” should return three where the default delimiter is ‘;’ .
    2. the first line is optional. all existing scenarios should still be supported
First the test.
calc_test.py
import nose.tools from calc import add  def test_empty_string_is_0():     assert 0 == add('')  def test_single_number_returns_value():     assert 1 == add('1')     assert 10 == add('10')  def test_multiple_numbers_returns_sum():     assert 3 == add('1,2')     assert 15 == add('10,5')     assert 25 == add('10,3,12')  def test_newline_is_a_delimiter():     assert 6 == add('1\n2,3')  def test_allow_custom_delimiter():     assert 3 == add('//;\n1;2') 
And now the code.
calc.py
def add(string):     return string and _sum_string(string) or 0  def _sum_string(string):     return ',' in string and _sum_delimited_string(string) or int(string)  def _sum_delimited_string(string):     string = _normalize_delimiters(string)     string_parts = string.partition(',')     return int(string_parts[0]) + _sum_string(string_parts[2])  def _normalize_delimiters(string):     if string.startswith('//'):         string_parts = string.partition('\n')         delimiter = string_parts[0]         delimiter = delimiter[2:]         string = string_parts[2].replace(delimiter, ',')     return string.replace('\n', ',') 
This fails, a bit unexpectedly. What went wrong here? The custom delimiter is not being parsed.

A little inspection and we realize that _sum_string() is to blame. Or more accurately, we see that we need to normalize the delimiters earlier in the process. So we move that call from _sum_delimited_string() into _sum_string(), which seems to make more sense anyway.
calc.py
def add(string):     return string and _sum_string(string) or 0  def _sum_string(string):     string = _normalize_delimiters(string)     return ',' in string and _sum_delimited_string(string) or int(string)  def _sum_delimited_string(string):     string_parts = string.partition(',')     return int(string_parts[0]) + _sum_string(string_parts[2])  def _normalize_delimiters(string):     if string.startswith('//'):         string_parts = string.partition('\n')         delimiter = string_parts[0]         delimiter = delimiter[2:]         string = string_parts[2].replace(delimiter, ',')     return string.replace('\n', ',') 
We are green again, but I don't think I can take it anymore. Look at all those [n] references in the code. And while I am not terribly concerned about it, the recursion could give us trouble on huge strings. There has to be a better way to do this.

So let's go back to the documentation and see if there is anything else we can use to assist us along. One of our problems is the partition breaks the string into three chunks. Maybe something that gives us just the values and strips out the delimiter. Or maybe something that breaks it into multiple chunks.

We discover split(). This appears to have some promise. It can chunk up the entire string into a list. That's nice. Maybe we can ditch the recursive call.

First round, let's get rid of the partition() call and replace it with split().
calc.py
def add(string):     return string and _sum_string(string) or 0  def _sum_string(string):     string = _normalize_delimiters(string)     return ',' in string and _sum_delimited_string(string) or int(string)  def _sum_delimited_string(string):     string_parts = string.split(',', 1)     return int(string_parts[0]) + _sum_string(string_parts[1])  def _normalize_delimiters(string):     if string.startswith('//'):         delimiter, string = string.split('\n', 1)         delimiter = delimiter[2:]         string = string.replace(delimiter, ',')     return string.replace('\n', ',') 
But this didn't seem to do anything for us. The code doesn't really look any better. And it still "feels" messy. We've two different functions that both claim to sum a string, but only one of them actually does. And these functions call one another. Let's push the responsibility back into a single function and see if we can clean this up.
calc.py
def add(string):     return string and _sum_string(string) or 0  def _sum_string(string):     string = _normalize_delimiters(string)     if ',' in string:         string_parts = string.split(',', 1)         return int(string_parts[0]) + _sum_string(string_parts[1])     else:         return int(string)  def _normalize_delimiters(string):     if string.startswith('//'):         delimiter, string = string.split('\n', 1)         delimiter = delimiter[2:]         string = string.replace(delimiter, ',')     return string.replace('\n', ',')  
That is better. But isn't that recursive call really just iterating over the list and converting everything to an int() and then adding them up? Looks a bit like a Scheme function with string_parts[0] and string_parts[1] in place of car and cdr. I know this is crying out for a map, but how do I explain this to Jennifer? How do we naturally stumble upon the solution?

So we focus in on the split() function.  What does this do? It returns a list of the words in a string. So we dig into lists a bit to learn what they are all about. And we find there are a few functions that are very useful with lists: filter(), map(), and reduce(). Of these, map() and reduce() look like they might do us some good.

We can use map() to convert our list of strings to a list of integers. Then, we can use reduce() to add them all up. Sound good? Yes. Yes it does. But when we check the syntax on reduce(), we see a note that says to use the sum() function for adding items in a list.

And we've arrived at our intended destination.
calc.py
def add(string):     return string and _sum_string(string) or 0  def _sum_string(string):     string = _normalize_delimiters(string)     if ',' in string:         return sum(map(int, string.split(',')))     else:         return int(string)  def _normalize_delimiters(string):     if string.startswith('//'):         delimiter, string = string.split('\n', 1)         delimiter = delimiter[2:]         string = string.replace(delimiter, ',')     return string.replace('\n', ',')  
That looks a little better. And all tests still pass. Now, let's refactor some more to make it a bit more terse.
calc.py
def add(string):     return string and _sum_string(string) or 0  def _sum_string(string):     string = _normalize_delimiters(string)     return ',' in string and sum(map(int, string.split(','))) or int(string)  def _normalize_delimiters(string):     if string.startswith('//'):         delimiter, string = string.split('\n', 1)         string = string.replace(delimiter[2:], ',')     return string.replace('\n', ',') 

We decided this was a good breaking point for the session. We'd covered a lot of new material in a relatively short period of time.

Tonight, we do it again, starting over from scratch. And I suspect we will skip some of the steps as we already know map() is our desired route.









solution set calculator

Factoring trinomials

I stay up too late on Wednesday night. This is a common occurence in the middle of the week, I think it is a symptom of being engaged. Even when I go to bed on time I lie awake, excited about life. It is hard to sleep when you're in love.

But this means that on Thursday morning I am dragging. I keep guzzling coffee but to no avail. I mix up my students names, forget what time the period ends, and mess up several examples. Right now I am teaching polynomials to my algebra students and it is tough going. Frankly, it is boring. My students have told me this. But I have had no great epiphanies about how to make x^2+8x+7 interesting. Just looking at it I get bored. On Thursday I was in the middle of my lesson on factoring trinomials when Precious raised her hand.

" Ms. Bell, when are we going to use this in real life," she said interupting my example. I was halfway throug! h factoring the trinomial. I looked at her and drew a complete blank. In the moment I muttered something about her needing good grades to get into college. But can I confess that I thought about it the rest of the period and I couldn't figure out why in the world we factor trinomials. What's the point anyway? I think it is just to drive people crazy. Look at this problem:

Factor.

3x^2-34x+63

Instant headache. I know, I know, there are probably some strategies and shortcuts I could use. But why? Why go through all that just to rewrite that polynomial in a different way? I think we should just put it on the graphing calculator and use the trace button.

Anyway, I am still teaching my poor 9th graders how to factor. Imagine trying to factor when you do not how to divide. Yup. It takes the headache to an instant migraine. My students struggle to factor 4x! +16. They keep telling me 16 divided by 4 is 12. Sigh. Hope! fully so meone is making progress.

But there is still a third of the year left. After I finish teaching them about polynomials I am going to go back to teaching them how to divide :)



p.s. This post is dedicated to my student, Tiara who came to tutoring after school every day this week so she could learn this factoring stuff. She's the kind of student who makes me excited to teach each morning.


trinomials