text:
Operating a Class 31 with an operable steam heat boiler was most certainly on the agenda from day one ! , indeed we took ownership of a Spanner Mk 1 boiler long before we actually got our hands on a loco !
Wanting to recreate the 'steam heat experience' we obviously required a boiler , preferably with pumps , electrical cubicle , float switches , water & fuel tanks. It was 1990 and at that time we hadn't discovered 31162 ( which had a boiler fitted ) so we felt it prudent to acquire everything we needed for the future. There were still a few 31s with complete but isolated boilers running around, so the situation wasn't critical. In early 1991 we examined 31225 in Doncaster Works which was awaiting breaking. Being an ex Stratford / Immingham steam heat fitted machine she still a boiler and in superb condition. Everything was there and in reasonable condition, except for the water level gauge which was missing . These were often removed by steam preservationists as they were BR standard.
31225 stands in the scrap line with 37260 & 31284 at Doncaster Wks March..1991.
By chance we had some of our shareholders working at ' The Plant ' so once we expressed an interest we managed to get 31225 moved inside the shops for the all the steam heat equipment to be removed. The only bit we didn't get was the chimney and roof panel. At the same time the cab end skirting was removed at both ends for future use, in fact it will be now be used for 31418. Procurement was contacted at Derby and we made an offer. Negotiations lasted 14 months ! but eventually our first offer was accepted which once the VAT was added came to £313.13 !
31225's boiler now removed at Doncaster and later being stripped at Darley Dale , Peak Rail..1992.
At that time , our chosen Railway was not the MRC but Peak Rail , so over 3 days at the end of March 1992 it was all removed to Darley Dale. There , over many , many weekends , the boiler was stripped down to a shell , its steel work prepared , primed and top coated several times with ' heat resistant ' paint. All the equipment was removed to Cleethorpes to be overhauled by ourselves. Following some 'developments' at Peak Rail , it was decided to look elsewhere for a base , which is when the MRC stepped in...... and during March 1994 the boiler was moved to Swanwick. As by that time we had our eyes on 31162 which had a water / fuel tank in situ , we took a gamble and scrapped the tanks at Darley Dale. Both tanks if needed could have been manufactured anyway. Work continued on the pumps , float switches , burner , electrical cubicle but the actual boiler restoration took a back seat whilst our new arrival 31162 was worked upon.
Working on our newly arrived boiler at Darley Dale...1992.
The boiler wiring , train heating pipe etc were given a thorough examination and on Saturday 17th June 1995 the main steam pipe flange in No 1 end was blanked off and the steam pipe pressurised to 55 PSI, thankfully no leaks at all. We now needed to remove 31162's boiler and tanks so over a couple of days in early November 1995 , everything was undone ready to lift out.
Friday 24th/Saturday 25th November saw both the tanks & boiler lifted out in major exercise which involved 25321 and the permanent way department steam crane. This boiler would become our spare , just incase. The fuel tank as expected was in superb condition and following cleaning out , was repainted and stored for re-fitting. The water tank was a different matter altogether ! To be honest we'd have probably been better making a new one...... the base was rotten and the baffles had disintegrated. Over the next few months , an access hole was burnt out , all the debris removed , tank cleaned out and new baffles welded in. The floor was renewed and the access hole welded back up. This was a major job !
Preparing the boiler for a lift at Swanwick...24/11/95.
It wasn't until late 1996 when work re-commenced on the boiler and the MRC boiler Inspector visited. Thankfully he was very happy with its condition , plate thickness and tubes , however a minor amount of wastage around the mud hole doors / water space gave him cause for concern. He was however quite happy to hydraulic the boiler to prove the tubes ( test was fine ) providing work was carried out on the wastage.
We managed to contract the very local ( and rail connected to Swanwick ) Butterley Engineering to carry out the rebuilding , which resulted in the lower 6 inches including mud holes and baseplate being completely renewed . We actually moved the boiler up to their works by rail ! In the meantime, because some of the wiring on the burner motor was to earth, the motor was sent away for an overhaul and soon returned looking as good as new.! Once repaired , on the 14th April 1997 the boiler was returned ( by rail once more ) to Swanwick where a few days later , to our relief , it passed its hydraulic test with flying colours.
Whilst all this was going on , the boiler compartment was given a thorough clean and repaint , some missing wiring replaced and all other wiring traced and tagged. The emergency shutdown switches were overhauled as were the alarm lights.
Following its thankfully successful hydraulic test , new firebrick and insulation was fitted followed by the repaired casing. Once painted, the overhauled float switches, water pump & motor, safety valve / burner / fuel pump & electrical cubicle were re-fitted and on Sunday 15th June 1997, 31225's boiler was fitted to 31162 with the tanks. Over the next year, in between working on 31108 & 31271 work continued refitting more parts back to the boiler and connecting and testing the wiring and circuits.
31162's overhauled boiler ( ex 31225 ) about to be craned in. Swanwick 15/06/97.
Underneath the loco , 31162's boiler fuel feed pipe had been removed in BR days so a replacement as well as the boiler flue was recovered from 31223 in 1994 , and were duly plumbed in during late 1998. Nearly there now ...! Following extensive testing of all the failsafe circuits , on Sunday 3rd January 1999 , water & fuel were added and the ' button ' pressed. In fact it's actually a switch.! but to our joy at 1400hrs , the boiler sprang into life and steamed for 3 hours ! Only 2 faults developed , one was the fuel solenoid which decided to go ' open circuit ' after 2 hours and the other being the fuel atomiser which started to leak badly near the nozzle. Having plenty of spares , these were replaced and repaired later.
31162 ( 5580 ) steams for the very first time in preservation. Swanwick.. 03/01/99.
A visit from the Insurance company on Thursday 21st January saw 31162 steam for 2 hours without fault and be credited with the necessary 14 month paperwork. We'd done it , a 31 steams again !
31162's first ( or it should be 5580's ) first passenger trains 'steaming' were over the weekend of 6th / 7th February 1999 at Butterley and following the 2 days it was pleasing to report no problems... ' hellfire ! '. Since that historic day the boiler has been re-certified every 14 months and has never let us down ....yet ! Everything has worked as it should. Fittings such as the safety valve , float switches, pressure switch have been removed for testing / re-calibration each year in line with current safety standards.
Unfortunately time ticks away so fast , and on 7th January 2008 , the hydraulic ticket expired so once again a thorough overhaul looms. During this overhaul , the tubes will be replaced.
Since 1999 when the boiler first steamed , 5580/31162 has steam heated 480 passenger trains on 47 separate days at 5 different railways without fault.
5580 stands at Wansford slowly losing steam pressure following a day heating trains.. 16/01/00 photo S Harvey
More steam in Golden Valley during a photo-stop on a birthday charter 17/01/01.
Spanner Boilers Ltd hail from Bracknell in Berkshire and were responsible for producing 3 types of boiler for British Railways : Mk 1 , Mk 2 & Mk 3.
The Mk 2 & 3 were smaller than the Mk 1 version and were fitted to deltics and class 47's respectively. The Mk 1 boiler was installed in all class 30 / 31 ( or Brush Type 2 ) as well as Class 28 ( Co Bo ) and some early NBL locos eg class 29. Later on , some boiler vans used Spanner Mk 1's removed from locomotives. Spanner used ' Swirlyflow ' tubes for their vertical oil fired Mk 1 which as the name implies , makes the hot exhaust gases swirl round in the tube , keeping the heat longer in the tube , therefore being more efficient. Two other companies ' Clayton ' and ' Stones ' also produced boilers for British Railways.
Drawings of a Spanner Mk 1 boiler.
Once the boiler switch is closed , the water pump will run and will draw water from the tank and commence filling the boiler. Once the water level reaches the low water cut -out , the Mobray float switch contacts will energise the burner motor. A fan is attached to the burner which blows air into the furnace to feed the fire. The fuel pump is also driven from the same shaft. The fuel atomiser and electrodes are situated within the burner. The burner is a converter and produces 240V AC which gets stepped up to 10000V AC in the transformer which produces a spark across 2 electrodes which instantly ignites the atomised fuel being sprayed. The magnita pictured in the illustration was removed and replaced with a transformer in the early 1980s.
The heat produced from the furnace passes through the tubes , heating the water which soon turns to steam. The water pump continues to run until the feed water regulator Mobray float switch is operated which stops the pump running . This float switch operates the water pump accordingly as the level rises and falls.....with the low water cut -out switch shutting down the boiler if for instance , the pump fails or there is insufficient water in the tank. The fluestat situated in the chimney stack will shut down the burner within a few seconds if it cannot detect heat . Under normal conditions when it does detect heat , it will operate to switch off the spark at the electrodes , the flame (now established and kept going by the fuel and air) will continue to burn.
Once the steam pressure has risen to 60 PSI , the pressure switch operates to switch off the burner , likewise when the pressure drops to 40 PSI it will run the burner again. The safety valve will lift at 75 PSI . The boiler if maintained correctly will generally ' look after itself ' when running however a careful eye needs to be kept. Water level can be monitered with the side glass and should there be an urgent requirement to shutdown the boiler , this can be safely done with a switch in each cab.
At the end of operation , once the steam pressure drops to 10 - 15 PSI the boiler must be ' blown down ' to remove sediment from the water space. This is achieved by opening the blow down valve .Once all the steam has vented , the boiler switch is opened to effect a complete shutdown.
Our boiler at Darley Dale during restoration in 1993 and more recently in 31162 in working order.


text:
CLIMATE CHANGE: INDIA'S PERCEPTIONS, POSITIONS, POLICIES AND POSSIBILITIES Jyoti K. Parikh and Kirit Parikh (*) Indira Gandhi Institute of Development Research
Senior Professor and Professor Emeritus respectively.
The ideas expressed in these case studies are those of the authors and do not necessarily represent views of the OECD or its Member countries
Climate Change: India's Perceptions, Positions Policies and Possibilities
In January 2001, the OECD held an expert seminar as part of a pilot project to investigate interactions between the long term agenda for climate change and sustainable development strategies. Experts from both OECD and developing countries attended. Participants identified issues and approaches, based on their regional perspectives, relevant to an evolving, equitable regime for addressing climate change, given various national circumstances, political interests, institutions and capacities to achieve sustainable development objectives. They stressed the importance of both climate mitigation and adaptation policy within a sustainable development framework. Discussions and presentations centred around two broad themes: • • Synergies and trade-offs between sustainable development objectives and long-term strategies to limit climate change. How to build analytical and implementation capacity in developing countries to maximise synergies at local, regional and global levels of decision-making.
To support seminar discussions, the OECD commissioned several papers (including this one) from nonOECD country experts; authors were asked to comment on key interactions between climate change and sustainable development from their own regional or national perspectives. This paper is being released as an informal working paper in the hope that it will continue to stimulate interest and discussions on these topics in other fora. The paper expresses the opinions of the author(s), and does...

subset: commoncrawl


text:
Gwen Stacy dreams about SpidermanFebruary 12th, 2012
Romantic and horny girl Gwen Stacy has dated Spiderman before, but they've never had an exciting sex on the top of the world. Dirty thoughts are creeping through her mind, and she imagines herself spreading legs on a huge dick, that Peter is pushing into her stretched wet cunt, while they're fucking on the top of the building. She looks quite excited with her panties, hanging out on her elongated leg, while big breasts are squeezed by the horny Spiderman, stuffing Gwen high above the ground.


text:
Backing up your Palm Profile data 8
Just like HotSyncing your old PalmOS device to a PC to backup your personal data, webOS devices offer a similar feature to backup your data. However, instead of backing up to a PC, webOS relies on a cloud-based solution that performs a daily automatic sync over your cellular or Wi-Fi connection. All of the data stored within your Palm Profile gets sent to a secure Palm server and is encrypted so only you have access to it (although there is no current online way to access the data, as it's only available if you need to re-download it to a different webOS device).
It's important to note that not all of the data on your phone is backed up, but rather just things like your contacts or calendar entries stored to your Palm Profile, your linked synergy accounts, the apps you have downloaded (and their location on your Launcher), and so much more. Please be aware that any files you have saved on to your USB drive, any progress you may have made in your games, or information you added to 3rd party apps are NOT included in this backup. See this Palm Support page article to learn exactly what is backed up via your Palm Profile, and you may want to familiarize yourself with WebOS Internals' Save/Restore Homebrew app to plug the gaps in what is and is not backed up. However, keep reading after the break to learn how to ensure that your Palm Profile data is being backed up on a regular basis
To ensure that your data is being backed up, open up the Backup App () and make sure that Backup is switched to "ON" in the top-right corner of the screen. Also, check the "Last backup" date listed on the top-left. If the date is not within the last 24 hours, the first thing you should do is tap the "Back up now" button on the bottom of the screen to initiate a manual backup. Keep an eye out for the next few days to see if the automatic backup is occurring (it should happen exactly 24 hours after your manual backup). If it is not, you should give Palm support a call or online chat because you may have an issue with your phone. One possible fix for this has been to turn Backup Off and then back On to try and get it back on a schedule. You will notice that when you turn it off, you will get a warning that your backup on Palm's servers will also be deleted, which is not an issue if you turn it right back on and manually initiate a new backup.
Unfortunately, there have been a number of incidents where the backup app won't allow you to turn it back on (it happened to this writer). In this situation, the only fix seems to be to do an Erase of your phone and re-download and set up your device. Make sure you know your Palm Profile's email address and password before doing that erase! Also, be sure to contact Palm before you do this because they will need to push an older saved version of your backup to your profile since you already erased your current one in the prior step. That is why it's a good idea to do a manual backup for a few days to establish a backup history for Palm to pull from, otherwise you run the risk of losing all your recent changes to you Palm Profile contacts, calendar, memos, etc. If that still doesn't fix your issue, you may need to take a trip to the webOS Doctor.
8 Comments
Using a program like Battery Saver to put your phone in airplane mode (over night) can cause you to miss your backup schedule. I found that turning backups off and on can sometimes change the scheduled time. Otherwise, using Mode Switcher to leave Wifi data on and turn off phone services can be a solution.
I also had an issue turning on backup services after turning them off. I was just about to give up and call the good WebOS Doc, when I began to delete some old tasks and memos. I then thought I'd try one last time to turn backups on...and it worked. Not exactly sure why, but something to try.
Now i Love my Pre and all - but to not have a "set backup time" option in this app is absolutely unforgivable. Seriously - I havent written code in 10 years and I could probably figure out how to add that in a day if given access.
For me and my wife: right before bed -> if you turn backup off, dismiss app, and then open and turn it back on again (then dismiss) it should backup sometime in the next 3-5 hours. Do this once and you'll never have to worry about it bogging you down in the middle of something! Thankfully i've 'cheated' my way into nightly 3:42am backups, but to not default to something like this or give a time option is a small example of where webOS is still learning.
but really - it's SO SIMPLE TO ADD!!!
I may be giving Palm more credit than is due, but forcing a time allows them to distribute backup times evenly over the course of the day to minimize peak load times on their servers. If everyone could set a time, most people would set this to obvious hours like midnight, and even minutes (like 12:00). Palm's servers would be idle at times like 3:42AM...
I have to say that this "backup" program leaves a lot to be desired ...i.e. its useless for me - it backs up so few things its just nuts - Palm - this is what you used to be really good at - you forgot your roots - why in gods name does Palm still not have a simple way to backup EVERYTHING on a PRE or PIXI - I mean how hard can this be - just a simple data dump of all relevant files onto the USB partition or something. We could do this 10yrs ago for christs sake. I cannot believe that Palm engineers actually use a PRE or PIXI - there is no way an engineer would put up with this silliness. And has anyone tried something as simple as importing all of ones outlooks contacts / notes onto a PRE - talk about a disaster - this should be Palm Basic 101. I just pray they get this basic stuff resolved in 2.0. I just cant fight the battle anymore.....:-(
I don't think it is perfect, but it sure is better then nothing. Anyone remember the old Palm OS days when our units would lose everything when our batteries failed and items weren't installed on ROM permanently.
To be honest, my Pre minus has been losing its profile settings lately and applications have been busy asking me to re-login and verify who I am. Weird and I suspect it is time to hard reset and start over fresh.
Hopefully webOS 2.0 will be arriving soon and ether way, thanks for the advice suggestions that are very helpful! Sorli...
I was looking for article like this since 6 hours - THANKS
But I have two questions - backup app uses only wi-fi connection? Never 3G? And second question: can I turn off automatic backup and use only manual backup? Thanks for answers.
Relying on palms backup is how I lost every memo entry I had stored on a palm device for the last 8 years. I had to do a restore and none of my memos where there. I strongly suggest noone rely on this as a backup. Use third party products for your important data.


text:
Supporting academic achievement through athletics
The mission of Scholar Athletes is to support academic achievement through athletics.
We strive to enhance the scholar-athlete experience, provide regular instruction to the coaching staff, and improve academic performance and personal growth. These collaborative efforts build skills, confidence, shape character, and enhance opportunities for success. Together we play to achieve!
Congratulations to all our honorees! On Wednesday, May 27th Scholar Athletes gathered at Hibernian Hall in Roxbury for its Third Annual Scholar Athletes Scholarship Banquet. The evening is to recognize and celebrate our incredible young people on their academic achievements. Scholar Athletes awarded five Zone members with full tuition college scholarships and 19 Zone members with $5,000 merit scholarships. Thanks to our amazing Staff, supporters and partners, our students will be graduating high school with a plan!
allBSA. Zone members, families, friends and supporters gathered on June 2nd at the historic Strand Theater in Dorchester to announce and recognize the 2014-15 Most Valuable Scholar Athletes (MVSA) and announce the 2015 allBSA recipients. These individuals epitomize what is means to be a scholar-athlete in their Zone, at school and on the field.
Follow Us online, join our conversations, engage with our team!


text:
Publisher's Description+
Don't have time to run all the various tools and scripts to keep your Mac running smoothly? MainMenu makes these tasks quick and easy, right from your menu bar. Rebuilding your Spotlight library for faster searching, repairing permissions, cleaning caches to improve application performance, and even more advanced settings - such as enabling and disabling the Dashboard - are no more than two clicks away. MainMenu is full of powerful maintenance tools to keep your Mac running like new, within a slick, simple interface.
What's new in this version:
- Added support for Mac OS X 10.5.x (Leopard)
- Portuguese Brazilian, Italian, and Polish localizations added. Thanks to all who helped. (Portuguese coming soon)


text:
Citizens and Community Change6th National Social Marketing ConferenceNUI Galway, April 16th 2014.
Recognising empowered Citizens and Communities can bring about Change in the way we live and the environments we design, this 6th National Social Marketing Conference brings world known social marketing change experts, Professor Gerard Hastings, UK and Professor Rebekah Russell-Bennett, Australia, together with researchers and leaders in the public and voluntary sector. The conference examines issues around change and the mutually-reinforcing and integrated strategies and actions required to encourage, support, and enable people to make better choices for themselves and their families (HI, 2014, p.24). Such participatory governance, indeed deliberative democracy innovation, talks to whole-of-the-community and whole-system-in-the-room processes to capture the diversity and distinctiveness of individuals and community stakeholders for democratic problem solving, priority setting, decision making, strategic large-scale action and open collaborative policy making. Whether working in Health or the Environment, the Citizens and Community Change conference 2014 objectives are:-
1. To support the emerging academic and practice based network of social marketing, social innovation, behaviour change and policy researchers in Ireland and Europe.
2. To present behaviour and community change researchers with a multidisciplinary platform to communicate and disseminate their research and findings.
3. To show case best practice Citizen and Community Change research, case studies and mobilisation.
4. To provide a unique networking opportunity for researchers and scholars who seek social and community change in the fields of health promotion, local and national government, transport, Healthy Cities, community initiatives, public safety, environmental protection, science outreach, communication and mobilisation, charities and non-profits sectors.
5. To foster collaborations and partnerships with a holistic view towards Healthy Ireland and EU and
6. To think strategically about innovative sustainable citizen and community change.


text:
Cuba as a Classroom
Annual summer trips educate University of Arizona students about the culture, politics and daily life of an isolated island nation.
Annual summer trips educate University of Arizona students about the culture, politics and daily life of an isolated island nation.
Complex relations between two modalities of state violence
The Sabbagh Lecture, presented by the UA School of Anthropology
Arizona's Hispanic Society in the Twenty-First Century
Book and exhibit commemorate community impacted by urban renewal project
Tucson City Council member Regina Romero proposes ordinance change that would end citations for park protestors
Turkey's Role in the Muslim-Arab World's Democratization Efforts
Their homeland's future is top of mind, and they plan to help
The National Commission on Education Equity and Excellence
Dr. J.E. Peterson speaks to the Tucson Committee on Foreign Relations
A conversation with the acclaimed documentarian reveals some of the reasons why he found the story of "Prohibition" so compelling.
The Setting for 9/11
Creating a better, post-January 8th system to protect the public and help persons with serious mental illness
The Battle of Hope vs. Fear
Volunteers are vital for success of state parks that had been slated to close.
collection categories
[Culture] [People and Places] [Politics]
[9/11] [American Graduate] [Animals] [Arizona] [Arts and Life] [Astronomy] [Arizona State University] [AZ Connections] [Arizona Week] [Books] [Border / Immigration] [Business] [Cannabis] [City Election Bios] [Civil Rights] [Copper] [Pima County] [Davis-Monthan Air Force Base] [Downtown Tucson] [Drugs] [Economy] [Education] [Election 2012] [Environment] [Events] [Fire] [Food] [Giffords Shooting] [Government] [Health & Medicine] [History] [Jan8 Healing] [Jobs] [Justice System] [Kids] [Legislature] [LGBT] [Marijuana] [Martha McSally] [Military] [Mining] [Monsoon] [Museums] [Music] [State & National Parks] [Native American] [News] [Barack Obama] [Opinion] [Parks] [Pima Community College] [Raúl Grijalva] [Raytheon] [Confronting Suicide] [Science] [9/11 Video] [Solar] [sound : fiction] [Sports] [Sprawl] [The Stairwell Sessions] [Streetcar] [Teachers' Voices] [Technology] [Tucson Electric Power] [Transportation] [Tucson] [University of Arizona] [UA+] [Higher Education] [U.S.] [Veterans] [Water] [Weather] [Wildlife] [Reid Park Zoo]


text: Betty Templeton | IBDB: The official source for Broadway Information IBDB: The official source for Broadway Information Official Broadway League sites: - sites - broadway.org broadwayleague.com broadwayfanclub.com kidsnightonbroadway.com stagespecs.org tonyawards.com Quick Search For: In This Category: All Show People/Org. Theatre Season Betty Templeton Performer Female Productions ProductionsDate of ProductionsBrigadoon[Musical, Comedy, Original] Performer: Betty Templeton [Singer]Mar 13, 1947 - Jul 31, 1948 Find out where Betty Templeton and are credited together © 2001-2015, The Broadway League, All Rights Reserved. (Copyright and Disclaimer)


text:
Pub manager Andrew Ibbotson is passionate about the safety and success of his pub's town.
Andrew, who runs The Silkstone Inn, in Barnsley, is an enthusiastic champion of the local initiatives in place which help to promote and encourage good business practice in the South Yorkshire town.
Supported by South Yorkshire Police, South Yorkshire Fire and Rescue and Barnsley Metropolitan Borough Council, the area runs an online community forum, called Barnsley Voice, which incorporates pubwatch and Best Bar None.
Andrew said: "Barnsley Voice is a retail community forum for all businesses in the town centre. As pubwatch members, we can access pictures and information about banned individuals and details of past and forthcoming meetings, as well as licensing
information and forms."
Meetings are held every month, at which members can raise matters of concern and where, together, they can plan an overall approach to local issues. The scheme helps to address specific challenges, as well as strategic objectives of concern to the area as a whole.
As well as the 70 pubs involved, police and council officers are represented, so businesses get the chance to work directly with the authorities to create a better place for residents, shoppers, customers, visitors and staff.
The town's Best Bar None scheme was also relaunched in Barnsley last year, thanks to extra funding for the police and council.
Andrew was particularly pleased about winning the 'best pub' category, as well as being overall winner for 2011!
Pictured (left to right): Police Community Support Officer Leanne Goodall, pub manager Andrew Ibbotson, Bill Smith (business partner Escapade Night Club), Elizabeth Kay (Barnsley Town Centre business development officer), Mat Trueman (owner Escapade Night Club) and Danny Stuart (Pubwatch chairman).


text:
The Style Gallery is a great new way for you to view, love, and share outfit photos. It's meant to showcase
all the expressive, creative, and inspirational personalities of our community!
Love you too!
Your email has been sent!
Your favorite outfit will be as good as new when you slip this dainty bracelet around your wrist! Made of golden-hued metal, the dazzling piece features a subtle, mint detailing. No matter what ensemble you're donning, accessorize with this bangle for a top-notch finishing touch.
Final Sale: This item cannot be returned or exchanged.
Sorry this item isn't available anymore
Remarkable fashion & decor, inspired by our community, & delivered with uncommon care.
Copyright ©2002-2015 ModCloth.com. All Rights Reserved.
Site Map
CA Notice
Privacy & Security
Terms of Use


text:
Your cart is empty!
This elevated toilet seat with you in mind. The tool-free removable padded arms allows this product to be used in a wide range or different bathroom configurations, and makes storage of this item easy. The heavy-duty molded plastic construction ensures adequate strength and durability. A clamping mechanism locks safely and securely onto your toilet to prevent shifting of the seat while transferring to and from this product. The elevated seat and padded arms will help individuals who need a boost sitting down or standing up from the toilet. For consumers using a wheelchair tool free removable arms allow for easy transfer to and from the raised toilet seat.
It's a good crapper user, it's down rite handy.
I like mine real good. It is very handy when needed.
This here gizzy is really handy. It' light and easy to handle. It is especialy handy when the need is there.. I do highly recomend it!~!
Drive Medical Knock Down Aluminum Shower Chair Or Commode With Casters - 1 Ea
Our Price:$134.15


text:
Five child rearing tips I learned from the Sopranos finale
After all the discussion there's been over the finale of The Sopranos, it surprises me that nobody has commented on the superb parenting skills shown by Tony and Carmella over the past seven years. Why, in just the finale alone, there were a number of parenting tips that I was able to take to heart. If you're a parent, I think you might find them useful as well.
Don't let your children park their SUV in the forest.
When I was a kid, nobody had SUV's. Also I grew up in the desert of Arizona, so piles of leaves were hard to find. I don't know if the dealer warns you about the fire-causing catalytic converter when you buy the car, but I'm glad I found this out before my kids started driving.
Let your kids make their own choices.
As long as they choose to be a doctor or a lawyer.
Nothing gets a boy out of his depression like a girl in high school and a job in the porno industry.
This really is more of a tip for mothers since most Dads are well aware of these sure-fire cures for twentysomething angst.
When teaching your kids to drive, spend extra time on parallel parking.
I never want to be stuck in a diner waiting to eat my onion rings just because my daughter couldn't park her car.
If you absolutely must leave your children in the car while it's still running, at least make sure their grandfather's head is not under the wheel.
This tip doesn't come from the Tony & Carm but I think it's good to remember, nevertheless.


text:
Native Acts Cover
Sovereignity Matters Cover
Joanne Barker
Native Acts: Law, Recognition, and Cultural Authenticity
Duke University Press, August 2011
Click for more information
Editor and introduction "For Whom Sovereignty Matters", Sovereignty Matters: Locations of Contestation and Possibility in Indigenous Struggles for Self-Determination.
Lincoln: University of Nebraska Press, 2005.
Click for more information
The Promise of Poststrucruralist Sociology
Clayton Dumont
The Promise of Poststructuralist Sociology: Marginalized Peoples and the Problem of Knowledge
State University of New York Press (May 8, 2008)
"The Politics of Scientific Objections to NAGPRA" Wicazo Sa Review: A Native American Studies Journal [University of Minnesota Press] 18:1 (Spring 2003).
Available on-line through Project MUSE.
Joanne Barker and Clayton Dumont, "Contested Conversations: Presentations, Expectations, and Responsibility at the National Museum of the American Indian." American Indian Culture and Research Journal 30(2) 2006.
Obama and the biracial factor - The battle for a new American majority cover
Andrew Jolivétte
Obama and the biracial factor - The battle for a new American majority
Obama and the Biracial Factor is the first book to explore the significance of mixed-race identity as a key factor in the election of President Obama and examines the sociological and political relationship between race, power, and public policy in the United States.
Cultural Representation in Native America Cover
Editor and Introduction Cultural Representation in Native America. (Berkeley: AltaMira Press, July 2006.)
Louisiana Creoles Cover
Louisiana Creoles: Cultural Recovery and Mixed Race Native American Identity. (Lanham: Lexington Books, January 2007.)
Editor "Examining the Regional and Multi-Generational Context of Creole and American Indian Identity" in Cultural Representation in Native America (Berkeley: AltaMira Press, 2006.)
"Migratory Movement: The Politics of Ethnic Community (Re) Construction Among Creoles of Color, 1920-1940" in the Ethnic Studies Review Winter 2005 Vol 28.2 (Tempe: National Association of Ethnic Studies).
American Indian Culture
and Research Journal
A Report on the Health and
Wellness of Multiracial
Youth in the Bay Area
"Displacement, Gentrification and the Politics of Conversion" in Hurricane Katrina, Un-Natural Disaster: Reflections and Prospects, edited by John Brown Childs. (Santa Cruz: The New Pacific Press, 2005).
Guest Editor, "Indigenous Landscapes Post-Katrina: Beyond Invisibility and Disaster. Volume 32 No. 2 2008, UCLA American Indian Culture and Research Journal.
Co-Author, "A Report on the Health and Wellness of Multiracial Youth in the Bay Area" Funded by the K & F Baxter Foundation, RIMI Program, and the National Center on Minority Health and Health Disparities, May 2008.
Melissa Nelson
Editor, Introduction, "Lighting the Sun of Our Future - How These Teachings Can Provide Illumination," and two chapters, "Re-Indigenizing Our Bodies and Minds With Native Foods," and "Mending the Split-Head Society With Trickster Consciousness," in Original Instructions: Indigenous Teachings For A Sustainable Future (Rochester: Inner Traditions/Bear & Company, 2008)
Original Instructions: Indigenous Teachings For A Sustainable Future Cover
(forthcoming) 2008. "Rivers of Memory, Lakes of Survival: Native American Water Traditions and the Anishinaabeg Nation". In Deep Blue: Critical Reflections on Nature Religion and Water. Edited by Sylvie Shaw & Andrew Francis. Equinox Publishing, London, England.
(forthcoming) 2008. "More Than One Mask: The Context of NAGPRA for Museums and Tribes". Co-authored with primary author Edward M. Luby. The American Indian Culture and Research Journal, UCLA.
2006. "Ravens, Storms, and the Ecological Indian at the National Museum of the American Indian". Wicaso Sa Review, 21, no. 2. Project MUSE
"Oral Tradition, Identity, and Inter-generational Healing Through the Southern Paiute Salt Songs." In Cultural Representation and Contestation in Native America. Edited by Andrew Jolivette (Berkeley: AltaMira Press, Forthcoming).
Salt Song Trail DVD
Melissa Nelson and Philip Klasky
"The Salt Song Trail." News From Native California,
Vol. 18, No. 4, Summer 2005.


text:
Home Energy Pros
Ventilation of attics is covered in depth; Climate and design considerations; Codes; Building science vs. codes; Moisture management: drainage planes, airflo...
Views: 194
Tags: building codes, building science, codes, design, moisture management, More…ventilation, water
Join Home Energy Pros
Welcome toHome Energy Pros
Sign Upor Sign In
Or sign in with:
Home Energy Pros was founded by the developers of Home Energy Saver Pro (sponsored by the U.S. Department of Energy,) and brought to you in partnership with Home Energy magazine. Home Energy Pros is sponsored by the Better Buildings Residential Network.
© 2015 Created by Lawrence Berkeley National Laboratory.
Badges |
Report an Issue |
Terms of Service
Please check your browser settings or contact your system administrator.


text:
The Little Golden Book of Retroviruses
By Diogenes (bio - articles ) | Jul 12, 2003
Britain's Telegraph reports on another educational advance.
The Independent Advisory Group on Teenage Pregnancy, which tracks the progress of the Government strategy, wants sex education for infants to be "rooted" by law in lessons on relationships. ... At present, many primary schools teach four- to seven-year-olds that "humans can produce offspring". Between seven and 11, pupils learn more about human reproduction and HIV.
And HIV. Great, isn't it, to think that the birds-and-the-bees talk now includes rectal bleeding and tissue tears, and the minds of children have this universe opened to them with protease inhibitors in the foreground.
An appeal from our founder, Dr. Jeffrey Mirus:
Dear reader: If you found the information on this page helpful in your pursuit of a better Catholic life, please support our work with a donation. Your donation will help us reach seven million Truth-seeking readers worldwide this year. Thank you!
Our Fall Campaign
Progress toward our year-end goal ($162,320 to go):
$200,000.00 $37,679.72
81% 19%
All comments are moderated. To lighten our editing burden, only current donors are allowed to Sound Off. If you are a donor, log in to see the comment form; otherwise please support our work, and Sound Off!
Posted by: ILM -
Jul. 17, 2003 7:31 AM ET USA
This is good, really good, but we will accomplish more by prayer and fasting than we will by articulating so well reality to those who will not listen.


text:
The Gift Collection
Here we feature our in-house designed bracelets as a convenient and easy way for you to start a charm bracelet. Whether you are looking for a gift for a loved one on a special occasion, or just a treat for yourself, this is a great place to start for a little help and inspiration! Charm Bracelets are a timeless present choice and something you can add to each year, constantly bringing a bracelet up-to-date with a life story. Whatever the occasion, be it a baby's Christening, a Graduation present or 21st we hope our collection will help you to select the perfect present to celebrate each of life's milestones.


text:
The Pohlite Blog
Carrie Bradshaw's Apartment Sells for $9.8 Million
I'm an encyclopedic fan of HBO's Sex and the City, having watched and rewatched its six seasons innumerable times. I'll admit that I wasn't a fan from the very beginning, but then again I was about 12 or 13 years old when the series began.
Still, once delving into the series in my senior year of high school and subsequently devouring it in my early college years, it became quite an obsession. I've gone as far as deciding which character I am - the writer and vintage lover in me points to Carrie, yet there are traces of Charlotte in my brunette locks and affinity for classic 60s silhouettes.
When I planned a trip to visit my best friend Jen in New York in the winter of 2008, I knew that one of my top destinations would be to find Carrie's brownstone. Several Google searches later, we made our way to Perry Street in the West Village and found a small crowd surrounding the Sex and the City mecca.
A chain and "No Tresspassing" sign was strewn across the railings at the bottom of the steps, but that didn't stop us from sitting on the bottom step and taking some photos.
The brownstone was put on the market in March for $9.65 million, and has already sold for $9.85 million.
When I first heard that the residence was on the market, I fantasized about having the ability to live there, and how fabulous it all would be. But in actuality, it would probably be more of a nightmare than anything.
Sex and the City bus tours once stopped at the brownstone, but they do not list it as a destination anymore. Still, plenty of people know where the brownstone is located and visit it when they are in New York.
It wouldn't be much fun having people constantly outside, taking pictures. But maybe that's the price you pay for living in a place that is a part of pop culture history.
A fabulous part of pop culture history.
This site uses Facebook comments to make it easier for you to contribute. If you see a comment you would like to flag for spam or abuse, click the "x" in the upper right of it. By posting, you agree to our Terms of Use.


text:
Dr. Robert J. Mittman hasn't seen this many runny noses and watery eyes in the past 10 years.
Mittman, who owns and operates the Allergy & Asthma Family Care Center in Bayside (with a location in Levittown, L.I.) attributes the rise of allergy sufferers to the delayed season.
"The season was so delayed; we're used to seeing allergies mid March to April but because of the cold winter and it becoming warm very quickly it caused an uprise of allergies."
According to Mittman, allergies are an overreaction to normally harmless substances. The body believes these substances to be bacteria or the enemy and forms antibodies to fight against them.
But for those wondering whether that scratchy throat is a cold or just allergies, Mittman admits that at times it may be difficult to tell, but he has some surefire clues. Watery/itchy eyes occurring the same time every year and a family history are determining factors for allergies, whereas a fever is a sign of an infection.
To lower your chances of an allergic reaction, Mittman cites avoidance as the main principle. If you have a cat allergy, don't have a cat in the house. If you are allergic to pollen and you're a runner, try running at night when the pollen counts are significantly lower. Wearing sunglasses will shield your eyes from pollen and will in turn stop itchy eyes. Using Boroleum® ointment under the nose will also aid in catching pollen and preventing it from entering the nasal passages.
Generally, if symptoms are mild, over-the-counter (OTC) allergy remedies are fine, but Mittman cautions that after a day or two, if it doesn't work, then it is time to see your physician. One point that Mittman stresses is that allergies should not be ignored.
"Allergies are not harmless, if left alone they will fester."
Untreated allergies can cause asthma and sinus congestion among many other illnesses. Allergies also leave the body susceptible to other illnesses
"The immune system becomes depressed; the body is sending soldiers for pollen dust instead of bacteria."
Although there is no cure for allergies, there are three treatments that Mittman advocates,
"Avoidance, Medication, and Allergy Immunotherapy," he said.
Immunotherapy is an all-natural remedy of exposing the body to small doses of a specific allergen subcutaneously (beneath the skin) to desensitize the body.
Though Mittman recognizes these three treatments, he still remains optimistic in "looking for more of a cure than a temporary treatment."
Dr. Robert J. Mittman's office is located 38-21 Bell Boulevard in Bayside. For more information, call 718-423-9300.


text:
Green Man choices for best literature of 2006
When Green Man did its Best of 2006 listings, the editorial staff did not actually choose the best literary works of the year as they had not really discussed what was the very best at that point. (Only criteria were that it had to have published in 2006 and it be available for you, the reader, to purchase. The latter criteria excluded lots of limited edition items which Green Man receives in copious amounts but that really didn't exist in a manner that they could be purchased by you.) So here is what they picked as the result of a complex process requiring many long nights in the Green Man Pub listening to the Neverending Session and consuming lots of Guinness before they came to an agreement on their choices.
For the Best Adult Novel, their choice is clearly Ellen Kushner's The Privilege of The Sword which works well on its own as a satisfying read and as part of her Riverside series -- no lover of superb literature should forego the pleasure of this novel. Small Beer Press published a limited edition hardcover edition which has to be held to be truly appreciated for its superb craftsmanship.
They picked Elizabeth Bear's Blood and Iron, the first volume of her Promethean Age series, for Best Fantasy Work. The Green Man review said it was a superb example of 'fantasy's answer to cyberpunk. It has that kind of fluidity and, more often than not, that kind of hard-edged, dark vision.'
For Best Gothic Ghost Novel, they picked Cherie Priest's Wings to the Kingdom, sequel to her brilliant first novel, Four and Twenty Blackbirds. They wholeheartedly agreed with what the review said:, 'Cherie Priest has a voice full of joy and wisdom. Like all good writers (and her own heroine Eden), she sees the dead. And she can tell their stories.'
Best Science Fiction Work of was the Tor trade paper editions of George Alec Effinger's The Budayeen Trilogy (When Gravity Fails, A Fire in the Sun, and The Exile Kiss). The editors noted that much of the really impressive science fiction they saw in the past year has been reissues of older works!
Best SF Novella of the year was Kage Baker's 'The Maid on the Shore' from her Dark Mondays collection. Superb. Kage also picks up an award for Best Ongoing Series which had a Novel Published in 2006 for The Machine's Child in which the story of The Company, the various immortal cyborgs, a pirate AI, and others get terribly complicated.
For Young Adult Novel, they chose Keiichi Sigsawa's Kino no Tabi which they note is is part of Tokyopop's new Pop Fiction imprint. They were utterly entranced by the premise, and by Kino herself... Weighty matters indeed, yet entertainingly presented. An excellent beginning to both a series and an imprint!
For Best Chapbook of the Year, they selected two chapbooks by Ray Bradbury, How I Wrote My Book, and The Wish , both from Hill House, but, as the Green Man reviewer said, ' these are mere appetizers for the main course of The Martian Chronicles, which will include material never before published before, including two Martian Chronicles film scripts written by Mr. Bradbury! For full details on this amazing edition, go here.' Both chapbooks are delights in and of themselves as well!
For Best Mystery, they selected, after some great debate as there were a number of worthy contenders for this award, Jason Goodwin's The Janissary Tree which the reviewer noted was 'Another murder mystery set in nineteenth-century Istanbul! Awesome!'
Best Alternative History Novel goes to Jo Walton's Farthing which also happens to be a rather brilliant mystery.
Catherynne M. Valente's The Orphan's Tales: In the Night Garden is neither a novel in the tradition sense of such a creature nor is it a collection of shorter fiction -- it's simply the Best Mythopoeic Work! Read it with an open mind on the matter of narrative forms and you'll be quite delighted with the story told here.
Best Genre-Buster Award goes to Alexander C. Irvine for The Narrows, an alternative telling of WWII complete with golems, and The Life of Riley, a novella that defies description in a few words! Indeed there's not much to be said about Irvine -- simply unique, that's all.
A reprint of the first issues of a ground breaking series was the suggestion of everyone for Best Graphic Novel. (They had a choice for Worst Graphic Novel as well, but the less said about that debacle, the better!) As was said in the GMR review, 'Without any doubt at all, the absolutely best graphic novel of 2006 is Neil Gaiman's Absolute Sandman Vol. 1! And it's one of the best literary efforts of the year as well.'
Feeling Very Strange is their clear choice for Outstanding Fiction Anthology. While there were a number of contenders, this one seems to have hit all the bases. Editors Kelly and Kessel put together an intelligent overview of the latest rebellion in a perennially rebellious field of literature, distinguished by the quality of the stories and their lack of interest in being categorized, with the added treat of discussions by the perpetrators about what it all means -- if it does.
Theodora Goss' 'The Rose in Twelve Petals,' from Feeling Very Strange was picked as the very Best Short Story which the Editorial staff said was 'a multi-layered retelling of Sleeping Beauty that occupies a territory between that fantasy world that fairy tales inhabit and the past and present of 'reality' as we know it.'
Their choice for Best Single Author Collection is Peter Beagle's The Line Between as every single eligible story in the book, and that does indeed mean every single one, wound up getting picked for one or another Best Fantasy Of The Year anthology -- some before appearing in The Line Between, some since, but nevertheless all. How many collections can claim that?
Best History/Autobiography is without a doubt Wonder's Child which is both the late Jack Williamson's autobiography and a history of science fiction from the inside.
There was no doubt 'tall in their minds as to what was the Best Reference Work published in 2006 -- Oxford Encyclopedia of Children's Literature, a four volume set edited by Jack Zipes which our Editor said of that 'Any library with a more than bare bones children's section needs this, as does anyone who is more than simply a fan of this literature. It is good enough that I expect to reference it a half dozen times a week. It's certainly worth its weight in silver doubloons, fairy gold, dragons teeth, and gossamer wings!'
Best Music Related Book was undoubtedly Michael Davidson's The Classical Piano Sonata from Haydn to Prokofiev -- a work marked by elegance, clarity, erudition, and some nice gossip.


text:
April 4, 2014
Star Entertainment
Music fraternity reacts to Kartel's sentence
Vybz Kartel (front) and Shawn Storm.
After last week's sentencing postponement, the world waited with bated breath until yesterday when Justice Lennox Campbell handed down his judgement in the historic Vybz Kartel case.
The fate of the popular dancehall deejay along with the three co-accused was finally revealed.
Kartel was sentenced to life in prison and would spend 35 years with hard labour before he would be eligible for parole. As the news broke, social media erupted with mixed reactions from Jamaicans in and across the Diaspora.
A Facebook fan page dedicated to Gaza fans only attracted a large number of comments from other users when it posted the news at approximately 12 p.m. yesterday. "BREAKING: Kartel sentenced to life in imprisonment with hard labour. Not eligible for parole before 35 years," was the post which garnered over 60 comments within the hour.
Another comment posted by the owners of the page showed signs of them hoping the judgement was a joke. "Smaddy tell the judge say April fools gone nuh". However, the page kept fans up to date with the court proceedings as the sentence was handed down.
Spice who did a collaboration with the deejay for the controversial Romping Shop tweeted, "so many media houses calling me for a comment on Kartel #LifeinPrison. 35 years before parole, I guess #RompingShop is the cause #SAD moment".
The entertainer was given 35 years in prison before eligibility for parole for the 2011 murder of Clive 'Lizard' Williams. His co-accused and member of the Gaza Empire Shawn 'Shawn Storm' Campbell was sentenced to 25 years, Kahira Jones, 25 years before eligible for parole and André St John, 30 years before being eligible for parole.
-Shereita Grizzle
Home | Gleaner Blogs | Gleaner Online | Go-Jamaica | Go-Local | Feedback | Disclaimer | Advertisement | Privacy Policy | Contact Us


text:
Functional Programming
So far, you've focused on familiarizing yourself with the tools that Clojure provides: immutable data structures, functions, abstractions, and so on. In this chapter, you'll begin learning how to think about your programming tasks in a way that makes the best use of those tools. You'll begin integrating your experience into a new mindset, the functional programming mindset.
The core concepts you'll learn include: what pure functions are and why they're useful; how to work with immutable data structures and why they're superior to their mutable cousins; how disentangling data and functions gives you more power and flexibility; and why it's powerful to program to a small set of data abstractions. The result of shoving all this knowledge into your brain matter is that you'll have an entirely new approach to problem solving!
After going over these topics you'll put everything you've learned to use in order to write a terminal-based game based on an ancient mystic mind-training device which can now be found in Cracker Barrel restaurants across America: Peg Thing®!
1. Pure Functions, What and Why
With the exception of
println and
rand, all the functions you've
used up till now have been pure functions:
(get [:chumbawumba] 0)
; => :chumbawumba
(reduce + [1 10 5])
; => 16
(str "wax on " "wax off")
; => "wax on wax off"
What makes them pure functions, and why does it matter? A function is pure if it meets two qualifications:
- It always returns the same result given the same arguments. This is called "referential transparency" and you can add it to your list of five-dollar programming terms.
- It doesn't cause any side effects, e.g. it doesn't "change the external world" by changing external mutable objects or outputting to i/o.
These qualities matter because they make it easier for us to reason about our programs. Pure functions are easier to reason about because they're completely isolated, unable to impact other parts of your system. When you use them, you don't have to ask yourself, "What could I break by calling this function?" They're also consistent; you'll never find yourself trying to figure out why passing a function the exact same arguments results in different return values.
For example, when was the last time you fretted over adding two numbers? Pure functions are as stable and problem-free as arithmetic. They're these stupendous, stable little bricks of functionality that you can confidently use as the foundation of your program. Let's look at referential transparency and lack-of-side-effects in more detail so that we'll know exactly what they are and how they're helpful.
1.1. Pure Functions Are Referentially Transparent
Referentially transparent functions always returns the same result when called with the same argument. In order to achieve this, they only rely on 1) their own arguments and 2) immutable values to determine their return value. Mathematical functions are referentially transparent:
(+ 1 2)
; => 3
If a function relies on an immutable value, it's referentially transparent. The string ", Daniel-san" is immutable, so the following function is referentially transparent:
(defn wisdom
(str words ", Daniel-san"))
(wisdom "Always bathe on Fridays")
; => "Always bathe on Fridays, Daniel-san"
By contrast, the following functions do not yield the same result with the same arguments and, therefore, are not referentially transparent. Any function which relies on a random number generator cannot be referentially transparent:
(defn year-end-evaluation
(if (> (rand) 0.5)
"You get a raise!"
"Better luck next year!"))
If your function reads from a file, it's not referentially
transparent because the file's contents can change. The function
analyze-file below is not referentially transparent, but the
analysis is:
(defn analysis
(str "Character count: " (count text)))
(defn analyze-file
(analysis (slurp filename)))
When using a referentially transparent function, you never have to consider what possible external conditions could affect the return value of the function. This is especially important if your function is used multiple places or if it's nested deeply in a chain of function calls. In both cases, you can rest easy knowing that changes to external conditions won't cause your code to break.
Another way to think about this is that reality is largely referentially transparent. This is what lets you form habits. If reality weren't referentially transparent, you wouldn't be able to mindlessly plug your iPod into your bathroom speakers and play "The Final Countdown" by Europe every morning when you take a shower. Each of these actions will have the same result pretty much every time you perform them, which lets you put them on autopilot.
1.2. Pure Functions Have No Side Effects
To perform a side effect is to change the association between a name and its value within a given scope. For example, in Javscript:
var haplessObject = {
emotion: "Carefree!"
var evilMutator = function(object){
object.emotion = "So emo :'(";
// => "So emo :'("
Of course, your program has to have some side effects; it writes to a disk, which is changing the association between a filename and a collection of disk sectors; it changes the rgb values of your monitor's pixels, and so on. Otherwise, there'd be no point in running it.
The reason why side effects are potentially harmful, though, is that they prevent us from being certain what the names in our code are referring to. This makes it difficult or impossible to know what our code is doing. It's very easy to end up wondering how a name came to be associated with a value and it's usually difficult to figure out why. When you call a function which doesn't have side effects, you only have to consider the relationship between the input and the output, not the changes that could be rippling through your system.
Functions which have side effects, however, place more of a burden on your mind grapes: now you have to worry about how the world is affected when you call the function. Not only that, every function which depends on a side-effecting function gets "infected". It's another component which requires extra care and thought as you build your program.
If you have any significant experience with a language like Ruby or Javascript, you've probably run into this problem. As an object gets passed around, its attributes somehow get changed and you can't figure out why. Then you have to buy a new computer because you've chucked yours out the window. If you've read anything about object-oriented design, you'll notice that a lot of writing has been devoted to strategies for managing state and reducing side effects for just this reason.
Therefore, it's a good idea to look for ways to limit the use of side effects in your code. Think of yourself as an overeager bureaucrat, — let's call you Kafka Human — scrutinizing each side effect with your trusty BureauCorp clipboard in hand. Not only will this lead to better code, it's also sexy and dramatic!
Luckily for you, Clojure makes your job easier by going to great lengths to limit side effects — all of its core data structures are immutable. You cannot change them in place no matter how hard you try! If you're unfamiliar with immutable data structures, however, you might feel like your favorite tool has been taken from you. How can you do anything without side effects? Well, guess what! That's what the next sections are all about! How about this segue, eh? Eh?
2. Living with Immutable Data Structures
Immutable data structures ensure that your code won't have side effects. As you now know with all your heart, this is a good thing. But how do you get anything done without side effects?
2.1. Recursion instead of for/while
Raise your hand if you've ever written something like this (Javascript):
var wrestlers = getAlligatorWrestlers();
var totalBites = 0;
var l = wrestlers.length;
// Side effect on i! Bad!
for(var i=0; i < l; i++){
// Side effect on totalBites! Bad!
totalBites += wrestlers[i].timesBitten;
or this:
var allPatients = getArkhamPatients();
var analyzedPatients = [];
var l = allPatients.length;
// Side effect on i! Bad!
for(var i=0; i < l; i++){
// Side effect on analyzedPatients! Bad!
Using side effects in this way — mutating "internal" variables — is pretty much harmless. You're creating some value to be used elsewhere, as opposed to changing an object you've received.
But Clojure's core data structures don't even allow these harmless mutations. So what can you do?
First, ignore the fact that you can easily use
map and
reduce to
accomplish the work done above. In these situations — iterating
over some collection to build a result — the functional
alternative to mutation is recursion.
Let's look at the first example, building a sum. In Clojure, there is no assignment operator. You can't associate a new value with a name within the same scope:
(defn no-mutation
(println x)
;; let creates a new scope
(let [x "Kafka Human"]
(println x))
;; Exiting the let scope, x is the same
(println x))
(no-mutation "Existential Angst Person")
; =>
; Existential Angst Person
; Kafka Human
; Existential Angst Person
In Clojure, we can get around this apparent limitation through recursion. The following example shows the general approach to recursive problem-solving.
(defn sum
([vals] (sum vals 0)) ;; ~~~1~~~
([vals accumulating-total]
(if (empty? vals) ;; ~~~2~~~
(sum (rest vals) (+ (first vals) accumulating-total)))))
This function takes two arguments, a collection to process (
and an accumulator (
accumulating-total), and we use arity
overloading (covered in Do Things) to provide a
default value of 0 for
accumulating-total (1).
Like all recursive solution, this function checks the argument it's
processing against a base condition. In this case, we check whether
vals is empty (2). If it is, we know that we've processed all the
elements in the collection and so we return
vals isn't empty it means we're still working our way through the
sequence, so we recursively call
sum with the "tail" of vals with
(rest vals) and the sum of the first element of vals and the
accumulating total with
(+ (first vals) accumulating-total). In this
way, we build up
accumulating-total and at the same time reduce
vals until it reaches the base case of an empty collection.
Here's what the recursive function call might look like:
(sum [39 5 1]) ; single-arity body calls 2-arity body
(sum [39 5 1] 0)
(sum [5 1] 39)
(sum [1] 44)
(sum [] 45) ; base case is reached, so return accumulating-total
; => 45
Each recursive call to
sum creates a new scope where
vals and
accumulating-total are bound to different values, all without
needing to alter the values originally passed to the function or
perform any internal mutation. As you can see, you can get along fine
without mutation.
One note: you should generally use
recur when doing recursion for
performance reasons. This is because Clojure doesn't provide tail call
optimization, a topic we will never bring up again! So here's how
you'd do this with
(defn sum
(sum vals 0))
([vals accumulating-total]
(if (empty? vals)
(recur (rest vals) (+ (first vals) accumulating-total)))))
recur isn't too important if you're recursively operating on a
small collection, but if your collection contains thousands or
millions values then you will definitely need to whip out
One last thing! You might be thinking, "Wait a minute, what if I end up creating thousands of intermediate values? Doesn't this cause the program to thrash because of garbage collection or whatever?
Very good question, eagle-eyed reader! The answer is no! This is because, behind the scenes, Clojure's immutable data structures are implemented using something called structural sharing, which is totally beyond the scope of this book. Just, uh, it's kind of like git! I don't know, google it!
2.2. Functional Composition instead of Attribute Mutation
Another way you might be used to using mutation is to build up the final state of an object. In the Ruby below example, the GlamourShotCaption object uses mutation to clean input by removing trailing spaces and capitalizing "lol":
class GlamourShotCaption
attr_reader :text
def initialize(text)
@text = text
def clean!
text.gsub!(/lol/, "LOL")
best = GlamourShotCaption.new("My boa constrictor is so sassy lol! ")
; => "My boa constrictor is so sassy LOL!"
GlamourShotCaption encapsulates the knowledge of how to clean a
glamour shot caption. On creating a
GlamourShotCaption object, you
assign text to an instance variable and progressively mutate it. So
far so good, right? The example below shows how you might do this in
Clojure. It uses
require in order to allow you to access the string
function library, a concept that will be covered in the next chapter.
(require '[clojure.string :as s])
(defn clean
(s/replace (s/trim text) #"lol" "LOL"))
(clean "My boa constrictor is so sassy lol! ")
; => "My boa constrictor is so sassy LOL!"
Easy peasy. No mutation required. Instead of progressively mutating an object, you apply a chain of functions to an immutable value. Incidentally, recursion is just a special case of function chaining: you're just chaining the same function over and over.
This example also starts to show the limitations of object-oriented
programming. In OOP, one of the main purposes of classes is to provide
data hiding — something that isn't necessary with immutable data
structures. You also have to tightly couple methods with classes, thus
limiting the reusability of the methods. In the Ruby example, you have
to do extra work to reuse the
clean! method. In Clojure,
will work on any string at all. By both a) decoupling functions and
data and b) programming to a small set of abstractions, you end up
with more reusable, composable code. You gain power and lose nothing.
If you think that this is a trivial example and not realistic, then consider all the times you've created very simple Ruby classes which essentially act as decorated hashes, but which aren't allowed to take part in the hash abstraction without work. The takeaway here is that you can just use function composition instead of a succession of mutations.
Once you start using immutable data structures you'll quickly feel confident in your ability to get stuff done. Then, you'll feel even more confident because you won't have to worry about what dirty code might be getting its greasy paws on your precious, mutable variables. It'll be great!
3. Cool Things to do With Pure Functions
Because you only need to worry about the input/output relationship in pure functions, it's safe to compose them. Indeed, you will often see code that looks something like this:
(defn dirty-html->clean-md
(html->md (tidy (clean-chars dirty-html))))
This practice is so common, in fact, that there's a function for
composing functions,
((comp clojure.string/lower-case clojure.string/trim) " Unclean string ")
; => "unclean string"
The Clojure implementation of this function can compose any number of functions. Here's an implementation which composes just two functions:
(defn two-comp
[f g]
(fn [& args]
(f (apply g args))))
I encourage you to try this out! Also, try re-implementing Clojure's
comp so that you can compose any number of functions.
Another cool thing you can do with pure functions is memoize them. You can do this because, as you learned above, pure functions are referentially transparent:
;; + is referentially transparent. You can replace this...
(+ 3 (+ 5 8))
;; ...with this...
(+ 3 13)
;; ...or this...
;; and the program will have the same behavior
Memoization lets you take advantage of referential transparency by storing the arguments passed to a function and the return value of the function. That way, subsequent calls to the function with the same arguments can return the result immediately. For example, in this un-memoized function, the result is returned after 1 second:
(defn sleepy-identity
"Returns the given value after 1 second"
(Thread/sleep 1000)
(sleepy-identity "Mr. Fantastico")
; => "Mr. Fantastico" after 1 second
(sleepy-identity "Mr. Fantastico")
; => "Mr. Fantastico" after 1 second
If you create a new, memoized version of
sleepy-identity with
memoize, however, then only the first call waits 1 second; every
subsequent function call returns immediately:
(def memo-sleepy-identity (memoize sleepy-identity))
(memo-sleepy-identity "Mr. Fantastico")
; => "Mr. Fantastico" after 1 second
(memo-sleepy-identity "Mr. Fantastico")
; => "Mr. Fantastico" immediately
Pretty cool!
4. Peg Thing®
Let's look at these ideas and techniques in a larger system. For the rest of the chapter, we'll create a terminal based game, Peg Thing®. As mentioned in the chapter introduction, this game is based on the secret mind sharpening tool passed down from Ye Olden Days and now distributed by Cracker Barrel, Inc.
If you're not familiar with the game, here are the mechanics. First, you start out with triangular board consisting of holes filled with pegs and with one hole missing a peg, like this:
The object of the game is to remove as many pegs as possible and you do this by "jumping" over pegs. In the following diagram, peg A jumps over peg B into the empty hole, removing peg B from the board:
Below is all the code for Peg Thing®. You can also find it on github. After a brief overview, we'll look at the code in detail.
(ns pegthing.core
(require [clojure.set :as set])
(declare successful-move prompt-move game-over query-rows)
;; Create the board
(defn tri*
"Generates lazy sequence of triangular numbers"
([] (tri* 0 1))
([sum n]
(let [new-sum (+ sum n)]
(cons new-sum (lazy-seq (tri* new-sum (inc n)))))))
(def tri (tri*))
(defn triangular?
"Is the number triangular? e.g. 1, 3, 6, 10, 15, etc"
(= n (last (take-while #(>= n %) tri))))
(defn row-tri
"The triangular number at the end of row n"
(last (take n tri)))
(defn row-num
"Returns row number the position belongs to: pos 1 in row 1,
positions 2 and 3 in row 2, etc"
(inc (count (take-while #(> pos %) tri))))
(defn in-bounds?
"Is every position less than or equal the max position?"
[max-pos & positions]
(every? (fn [pos] (>= max-pos pos)) positions))
(defn connect
[board max-pos pos neighbor destination]
(if (in-bounds? max-pos neighbor destination)
(reduce (fn [new-board [p1 p2]] (assoc-in new-board [p1 :connections p2] neighbor))
[[pos destination] [destination pos]])
(defn connect-right
[board max-pos pos]
(let [neighbor (inc pos)
destination (inc neighbor)]
(if-not (or (triangular? neighbor) (triangular? pos))
(connect board max-pos pos neighbor destination)
(defn connect-down-left
[board max-pos pos]
(let [row (row-num pos)
neighbor (+ row pos)
destination (+ 1 row neighbor)]
(connect board max-pos pos neighbor destination)))
(defn connect-down-right
[board max-pos pos]
(let [row (row-num pos)
neighbor (+ 1 row pos)
destination (+ 2 row neighbor)]
(connect board max-pos pos neighbor destination)))
(defn add-pos
"Pegs the position and performs connections"
[board max-pos pos]
(let [pegged-board (assoc-in board [pos :pegged] true)]
(reduce (fn [new-board connector] (connector new-board max-pos pos))
[connect-right connect-down-left connect-down-right])))
(defn new-board
(let [initial-board {:rows rows}
max-pos (row-tri rows)]
(reduce (fn [board pos] (add-pos board max-pos pos))
(range 1 (inc max-pos)))))
;; Move pegs
(defn pegged?
[board pos]
(get-in board [pos :pegged]))
(defn valid-moves
"Return a map of all valid moves for pos, where the key is the
destination and the value is the jumped position"
[board pos]
(into {}
(filter (fn [[destination jumped]]
(and (not (pegged? board destination))
(pegged? board jumped)))
(get-in board [pos :connections]))))
(defn valid-move?
"Return jumped position if the move from p1 to p2 is valid, nil
[board p1 p2]
(get (valid-moves board p1) p2))
(defn remove-peg
[board p]
(assoc-in board [p :pegged] false))
(defn add-peg
[board p]
(assoc-in board [p :pegged] true))
(defn move-peg
"Take peg out of p1 and place it in p2"
[board p1 p2]
(add-peg (remove-peg board p1) p2))
(defn make-move
"Move peg from p1 to p2, removing jumped peg"
[board p1 p2]
(if-let [jumped (valid-move? board p1 p2)]
(move-peg (remove-peg board jumped) p1 p2)))
(defn can-move?
"Do any of the pegged positions have valid moves?"
(some (comp not-empty (partial valid-moves board))
(map first (filter #(get (second %) :pegged) board))))
;; Represent board textually and print it
(def alpha-start 97)
(def alpha-end 123)
(def letters (map (comp str char) (range alpha-start alpha-end)))
(def pos-chars 3)
(def ansi-styles
{:red "[31m"
:green "[32m"
:blue "[34m"
:reset "[0m"})
(defn ansi
"Produce a string which will apply an ansi style"
(str \u001b (style ansi-styles)))
(defn colorize
"Apply ansi color to text"
[text color]
(str (ansi color) text (ansi :reset)))
(defn render-pos
[board pos]
(str (nth letters (dec pos))
(if (get-in board [pos :pegged])
(colorize "0" :blue)
(colorize "-" :red))))
(defn row-positions
"Return all positions in the given row"
(range (inc (or (row-tri (dec row-num)) 0))
(inc (row-tri row-num))))
(defn row-padding
"String of spaces to add to the beginning of a row to center it"
[row-num rows]
(let [pad-length (/ (* (- rows row-num) pos-chars) 2)]
(apply str (take pad-length (repeat " ")))))
(defn render-row
[board row-num]
(str (row-padding row-num (:rows board))
(clojure.string/join " " (map (partial render-pos board) (row-positions row-num)))))
(defn print-board
(doseq [row-num (range 1 (inc (:rows board)))]
(println (render-row board row-num))))
;; Interaction
(defn letter->pos
"Converts a letter string to the corresponding position number"
(inc (- (int (first letter)) alpha-start)))
(defn get-input
"Waits for user to enter text and hit enter, then cleans the input"
([] (get-input nil))
(let [input (clojure.string/trim (read-line))]
(if (empty? input)
(clojure.string/lower-case input)))))
(defn characters-as-strings
"Given a string, return a collection consisting of each indivisual
(re-seq #"[a-zA-Z]" string))
(defn prompt-move
(println "\nHere's your board:")
(print-board board)
(println "Move from where to where? Enter two letters:")
(let [input (map letter->pos (characters-as-strings (get-input)))]
(if-let [new-board (make-move board (first input) (second input))]
(successful-move new-board)
(println "\n!!! That was an invalid move :(\n")
(prompt-move board)))))
(defn successful-move
(if (can-move? board)
(prompt-move board)
(game-over board)))
(defn game-over
(let [remaining-pegs (count (filter :pegged (vals board)))]
(println "Game over! You had" remaining-pegs "pegs left:")
(print-board board)
(println "Play again? y/n [y]")
(let [input (get-input "y")]
(if (= "y" input)
(println "Bye!")
(System/exit 0))))))
(defn prompt-empty-peg
(println "Here's your board:")
(print-board board)
(println "Remove which peg? [e]")
(prompt-move (remove-peg board (letter->pos (get-input "e")))))
(defn prompt-rows
(println "How many rows? [5]")
(let [rows (Integer. (get-input 5))
board (new-board rows)]
(prompt-empty-peg board)))
(defn -main
[& args]
(println "Get ready to play peg thing!")
The functions are separated into four sections by comment blocks: one section for creating the board, one for moving pegs, one for representing the board textually and printing it, and one for handling input and game flow. Before all that, though, there's this:
(ns pegthing.core
(require [clojure.set :as set])
(declare successful-move prompt-move game-over query-rows)
All of this gets explained in the next chapter. If you're curious
about what's going on, the short explanation is that know that
(require [clojure.set :as set]) allows you to easily use functions
in the
clojure.set namespace,
(:gen-class) allows you to run the
program from the command line, and
(declare successful-move
prompt-move game-over query-rows) allows functions to refer to those
names before they're defined. If that doesn't quite make sense yet,
just skip it for now and trust that it will get covered soon.
4.1. Playing
To start Peg Thing, just run
lein run in your terminal while in the
project's root directory. That will present you with a prompt that
looks like this:
Get ready to play peg thing!
How many rows? [5]
You can enter in the number of rows the board will have, with 5 as the default. If you want 5 rows, just hit enter, otherwise type a number and hit enter. After you do that, you'll see this:
Here's your board:
b0 c0
d0 e0 f0
g0 h0 i0 j0
k0 l0 m0 n0 o0
Remove which peg? [e]
Each letter identifies a position on the board. The number
indicates that a position is filled. The prompt is asking you to enter
the position of the first to peg to remove, with a default of
After you remove the peg, this is what you'll see:
Here's your board:
b0 c0
d0 e- f0
g0 h0 i0 j0
k0 l0 m0 n0 o0
Move from where to where? Enter two letters:
Notice that the
e position now has a dash,
-. This indicates that
the position is empty. In order to move, you enter the position of the
peg you want to "pick up" followed by the position of the empty
position that you to place it in. If you enter
le, for example,
you'll get this:
Here's your board:
b0 c0
d0 e0 f0
g0 h- i0 j0
k0 l- m0 n0 o0
Move from where to where? Enter two letters:
The game continues to prompt you for moves until no moves are available, whereupon it prompts you to play again.
Now that you know how it's supposed to work, let's go through the code!
4.2. Creating the Board
There are many ways that you could represent the board, but in this
case you're going to represent the board with a map with numerical
keys corresponding to each board position and values containing
information about that positions connections. The map will also
contain a
:rows key, storing the total number of rows. Below is a
board with each position numbered, followed by the data structure
built to represent it:
{1 {:pegged true, :connections {6 3, 4 2}},
2 {:pegged true, :connections {9 5, 7 4}},
3 {:pegged true, :connections {10 6, 8 5}},
4 {:pegged true, :connections {13 8, 11 7, 6 5, 1 2}},
5 {:pegged true, :connections {14 9, 12 8}},
6 {:pegged true, :connections {15 10, 13 9, 4 5, 1 3}},
7 {:pegged true, :connections {9 8, 2 4}},
8 {:pegged true, :connections {10 9, 3 5}},
9 {:pegged true, :connections {7 8, 2 5}},
10 {:pegged true, :connections {8 9, 3 6}},
11 {:pegged true, :connections {13 12, 4 7}},
12 {:pegged true, :connections {14 13, 5 8}},
13 {:pegged true, :connections {15 14, 11 12, 6 9, 4 8}},
14 {:pegged true, :connections {12 13, 5 9}},
15 {:pegged true, :connections {13 14, 6 10}},
:rows 5}
You might be wondering why, when you play the game, each position is represented by a letter but here the positions are represented by numbers. Letters are for humans and numbers are for the data structure, and there are some conversion functions covered below.
In the data structure, each position is associated with a map that reads something like:
{:pegged true, :connections {6 3, 4 2}}
The meaning of
:pegged is clear – it represents whether that
position has a peg in it.
:connections is a little more cryptic.
It's a map where each key identifies a possible destination, and
each value represents the position that would bejumped over. So pegs
in position 1, for example, can jump to position 6, over
position 3. This might seem a little backwards, but you'll see the
rationale for it later when you examine how move validation is
In order to get there, I'll walk through the code from top to bottom. This has the advantage that each bit of code will be fully understandable in itself, but the disadvantage of hiding the general approach taken. Therefore, I'll explain the general approach after all the code's been covered.
The first few expressions deal with triangular numbers. Triangular numbers are generated by adding the first n natural numbers. The first triangular number is 1, the second is 3 (1 + 2), the third is 6 (1 + 2 + 3), and so on. These numbers line up nicely with the position numbers at the end of every row on board, which will turn out to be a useful property. First, a function defines how to generate a lazy sequence of triangular numbers, as explained in the last chapter:
(defn tri*
"Generates lazy sequence of triangular numbers"
([] (tri* 0 1))
([sum n]
(let [new-sum (+ sum n)]
(cons new-sum (lazy-seq (tri* new-sum (inc n)))))))
The next expresion "initializes" the function, actually creating the lazy sequence:
(def tri (tri*))
You can verify that it actually works:
(take 5 tri)
; => (1 3 6 10 15)
And the next few functions operate on the sequence of triangular numbers. Here they are, along with example usage:
(defn triangular?
"Is the number triangular? e.g. 1, 3, 6, 10, 15, etc"
(= n (last (take-while #(>= n %) tri))))
(triangular? 5) ; => false
(triangular? 6) ; => true
(defn row-tri
"The triangular number at the end of row n"
(last (take n tri)))
(row-tri 1) ; => 1
(row-tri 2) ; => 3
(row-tri 3) ; => 6
(defn row-num
"Returns row number the position belongs to: pos 1 in row 1,
positions 2 and 3 in row 2, etc"
(inc (count (take-while #(> pos %) tri))))
(row-num 1) ; => 1
(row-num 5) ; => 3
The next function,
in-bounds? is used when figuring out whether or
not to connect to positions. For example, if you have 5 rows then the
max position is 15, but the math used to determine connections might
try to connect position 7 with position 16.
in-bounds? makes sure
your program doesn't do anything outrageous like this.
After that comes
connect, used to actually form a mutual connection
between two positions:
(defn connect
"Form a mutual connection between two positions"
[board max-pos pos neighbor destination]
(if (in-bounds? max-pos neighbor destination)
(reduce (fn [new-board [p1 p2]] (assoc-in new-board [p1 :connections p2] neighbor))
[[pos destination] [destination pos]])
(connect {} 15 1 2 4)
; =>
{1 {:connections {4 2}}
4 {:connections {1 2}}}
connect uses recursion through
reduce in order to progressively
"build up" the final state of the board. In the example above, you're
reducing over the nested vectors
[[1 4] [4 1]]. This is what allows
you to return an updated board with both
pos and
destination (1
and 4) pointing to each other in their connections.
The anonymous function passed to
reduce uses a function you haven't
seen before,
assoc-in. Whereas the function
get-in lets you look
up values in nested maps,
assoc-in allows you to return a new map
with the given value at the specified nesting. Here are a couple
(assoc-in {} [:cookie :monster :vocals] "Finntroll")
; => {:cookie {:monster {:vocals "Finntroll"}}}
(get-in {:cookie {:monster {:vocals "Finntroll"}}} [:cookie :monster])
; => {:vocals "Finntroll"}
(assoc-in {} [1 :connections 4] 2)
; => {1 {:connections {4 2}}}
In these examples, you can see that, new, nested maps are created to accommodate all the keys provided.
Another thing to notice about
connect is that it uses
to check whether all positions being associated are valid. Here's what
would happen if you tried to connect out-of-bounds positions:
(connect {} 15 7 11 16)
; => {}
Now that you have a way to connect to positions, you need a way to
figure out which positions to connect. That's handled by
connect-down-left, and
connect-down-right. These
functions each take a position and use a little triangle math to
figure out which numbers to feed to
connect. In case you're
wondering why there aren't
connect-up-left, and
connect-up-right functions, it's because the existing functions
actually cover these cases because
connect returns a board with the
mutual connection established; when 4 "connects right" to 6, 6
"connects left" to 4. Here are a couple examples:
(connect-down-left {} 15 1)
; =>
{1 {:connections {4 2}}
4 {:connections {1 2}}}
(connect-down-right {} 15 3)
; =>
{3 {:connections {10 6}}
10 {:connections {3 6}}}
The next function,
add-pos, is interesting because it actually
reduces on a vector of functions, applying each in turn to build up
the resulting board. But first, it updates the board to indicate that
a peg is in the given position:
(defn add-pos
"Pegs the position and performs connections"
[board max-pos pos]
(let [pegged-board (assoc-in board [pos :pegged] true)]
(reduce (fn [new-board connector] (connector new-board max-pos pos))
[connect-right connect-down-left connect-down-right])))
(add-pos {} 15 1)
{1 {:connections {6 3, 4 2}, :pegged true}
4 {:connections {1 2}}
6 {:connections {1 3}}}
Reducing over functions like this is another way of chaining
functions. To illustrate, here's another way of defining the
function in the glamour shot example above:
(defn clean
(reduce (fn [string string-fn] (string-fn string))
[s/trim #(s/replace % #"lol" "LOL")]))
clean like this doesn't provide any benefits, but in
add-pos it makes sense because each function takes multiple
argument. If you didn't use reduce, your code could end up looking
like this:
(defn add-pos
"Pegs the position and performs connections"
[board max-pos pos]
(let [pegged-board (assoc-in board [pos :pegged] true)]
(connect-right pegged-board max-pos pos)
max-pos pos)
max-pos pos)))
That's pretty confusing! Reducing over a collection of functions is not something you'll do often, but it does demonstrate the versatility of functional programming.
Last among our board creation functions is
new-board. It's actually
pretty straightforward, reducing over a collection of board positions
to build up the final board.
4.3. Moving Pegs
The next section of code deals with validating peg moves and actually
performing the move. Many of the functions (
move-peg) are simple one-liners which need no further
explanation, so let's start with
(defn valid-moves
"Return a map of all valid moves for pos, where the key is the
destination and the value is the jumped position"
[board pos]
(into {}
(filter (fn [[destination jumped]]
(and (not (pegged? board destination))
(pegged? board jumped)))
(get-in board [pos :connections]))))
This goes through each of the given position's connections and tests whether the destination position is empty and the jumped position has a peg. To see this in action, you can create a board with the 4 position empty:
(def my-board (assoc-in (new-board 5) [4 :pegged] false))
Here's what that board would look like:
Given this board, positions 1, 6, 11, and 13 have valid moves, and all others don't:
(valid-moves my-board 1) ; => {4 2}
(valid-moves my-board 6) ; => {4 5}
(valid-moves my-board 11) ; => {4 7}
(valid-moves my-board 13) ; => {4 8}
(valid-moves my-board 5) ; => {}
(valid-moves my-board 8) ; => {}
You might be wondering why
valid-moves returns a map instead of say,
a set or vector. The reason is that returning a map allows you to
easily look up a destination position to check whether a specific move
is valid, which is what
valid-move? does:
(defn valid-move?
"Return jumped position if the move from p1 to p2 is valid, nil
[board p1 p2]
(get (valid-moves board p1) p2))
(valid-move? my-board 8 4) ; => nil
(valid-move? my-board 1 4) ; => 2
Another nice benefit of having
valid-moves return a map is that
valid-move? can return the position of the peg that's jumped over.
The function
make-move makes use of this. By taking the time to
construct a rich data structure, it's easier to perform useful
(defn make-move
"Move peg from p1 to p2, removing jumped peg"
[board p1 p2]
(if-let [jumped (valid-move? board p1 p2)]
(move-peg (remove-peg board jumped) p1 p2)))
Finally, the function
can-move? is used to determine whether the
game is over by checking all pegged positions to see if they have any
moves available:
(defn can-move?
"Do any of the pegged positions have valid moves?"
(some (comp not-empty (partial valid-moves board))
(map first (filter #(get (second %) :pegged) board))))
You can break this down into two parts. First, you get a sequence of
all pegged positions with
(map first (filter #(get (second %)
:pegged) board)). Then, you call a predicate function on each
position, trying to find the first popsition which returns a truthy
value. The predicate function is created with
(comp not-empty
(partial valid-moves board)), and it uses two functions from the last
comp and
partial. The resulting function is equivalent to
these anonymous functions:
(fn [pos] (not-empty (valid-moves board pos)))
#(not-empty (valid-moves board %))
This just shows that there are often multiple ways to express the same idea in Clojure, and all of them are great!
4.4. Rendering and Printing the Board
The first few expressions in the board representation and printing section just define constants:
(def alpha-start 97)
(def alpha-end 123)
(def letters (map (comp str char) (range alpha-start alpha-end)))
(def pos-chars 3)
The bindings
alpha-start and
alpha-end are the beginning and end
of the numerical values of the laters a through z. We use those to
build up a seq of
char, when applied to an integer,
returns the character corresponding to that integer, and
str turns
the char into a string.
pos-chars is used by the function
row-padding to determine how much spacing to add to the beginning of
each row. The next few definitions,
ansi, and
colorize, all deal with outputting colored text to the terminal.
The next few functions,
render-row are all concerned with creating strings to represent
the board.
(defn render-pos
[board pos]
(str (nth letters (dec pos))
(if (get-in board [pos :pegged])
(colorize "0" :blue)
(colorize "-" :red))))
(defn row-positions
"Return all positions in the given row"
(range (inc (or (row-tri (dec row-num)) 0))
(inc (row-tri row-num))))
(defn row-padding
"String of spaces to add to the beginning of a row to center it"
[row-num rows]
(let [pad-length (/ (* (- rows row-num) pos-chars) 2)]
(apply str (take pad-length (repeat " ")))))
(defn render-row
[board row-num]
(str (row-padding row-num (:rows board))
(clojure.string/join " " (map (partial render-pos board) (row-positions row-num)))))
If you work from the bottom up, you can see that
render-row calls
each of the above function to return the string representation of row.
Notice the expression
(map (partial render-pos board) (row-positions
row-num)). This demonstrates a good use case for partials: applying
the same function multiple times with one or more arguments "filled
You'll also notice that
render-pos uses a letter to identify each
position, rather than a number. This is to save space a little in that
it allows a 5-row board to be represented with only one character per
print-board merely iterates over each row number with
doseq, printing the string representation of that row. You use
doseq when you want perform stateful operations on the elements of a
(defn print-board
(doseq [row-num (range 1 (inc (:rows board)))]
(println (render-row board row-num))))
4.5. Interaction
The last collection of functions handle player interaction. First,
letter->pos, which converts a letter to the corresponding
position number. This is necessary because the player identifies
positions with letters, but internally they're represented by numbers.
Next, the helper function
get-input allows you to read and clean the
player's input. You can also provide a default value, which is used if
the player hits enter without typing anything.
The next function,
characters-as-strings is a tiny helper function
used by
prompt-move to get all letters and discard non-alphabetic
(characters-as-strings "a b")
; => ("a" "b")
(characters-as-strings "a cb")
; => ("a" "c" "b")
prompt-move has read its input, it uses the following to act on
the move:
(if-let [new-board (make-move board (first input) (second input))]
(successful-move new-board)
(println "\n!!! That was an invalid move :(\n")
(prompt-move board)))
make-move returns nil if the player's move was invalid, and we
use that information to inform her of her mistake. If the move is
valid, however, the
new-board gets passed off to
which hands control back over to
prompt-move if there are still
moves to be made:
(defn successful-move
(if (can-move? board)
(prompt-move board)
(game-over board)))
In our board creation functions we saw how recursion was used to "build up" a value using immutable data structures. The same thing is happening here, only it involves two mutually recursive functions and some user input. No mutable attributes in sight!
The rest of the functions (
prompt-rows and
-main) are pretty straightforward, using all the
ideas we've covered so far, so I'm going to gloss right over them.
Moving along!
One final thing to note about the code as a whole: I tried as much as possible to separate pure functions from impure functions. This is a best practice for functional programming. By identifying the bits of functionality that are referentially transparent and side-effect free and placing those bits in their own functions, you're better able to reap the benefits of pure functions. These functions are not capable of causing bizarre bugs in unrelated parts of your program. They're easier to test and develop in the REPL because they only rely on the arguments you pass them, and not on some complicated hidden state object.
5. Chapter Summary
Pure functions are referentially transparent and side-effect free. This makes them easy to reason about. To get the most out of Clojure, try to keep your dirty, impure functions to a minimum. In an immutable world, you use recursion instead of for/while and function composition instead of successions of mutations. Pure functions allow powerful techniques like function composition functions and memoization. They're also super fun!


text:
Allegations of Widespread Fraud in Egyptian Elections
Following the release of the official results of the first round of the post-Mubarak Egyptian elections, allegations of fraud are rampant on the ground.
Next month's second round run-off election between Mohammed Morsi, a Muslim Brotherhood candidate, and Ahmed Shafik, an old guard general and hold over from the Mubarak regime, has been described as a "nightmare scenario" for the Egyptian people. George Ishaq, a founding member of the left leaning Kifaya Party decried the results:
"It feels as if the revolution never took place. The Brotherhood are despotic and fanatical and Shafiq is the choice of Mubarak. It is a very bad result. The revolution is not part of this contest."
Polls indicate that Morsi is the current front runner, but numerous politicians have expressed a lack of confidence in the election results. Abdel Moneim Abol Fotouh, a former member of the Muslim Brotherhood who came in fourth in the polls during the first round of elections, has stated categorically that he refuses to recognize the legitimacy of the election results, regardless of who emerges as the winner.
"I reject these results and do not recognize them," he said, further claiming that he possesses proof that representatives of various candidates were denied access to polling places, and that in many instances votes were bought and paid for.
Hamdeen Sabahi, a socialist candidate who is popularly viewed as a true representative of the revolution, has called for a partial recount following his third place finish behind Shafik by roughly 700,000 votes. "The difference between votes for us and votes cast for some of the other candidates is that ours are legitimate," he stated on Saturday.
Sabahi's lawyer, Essam El-Islamboly, intends to petition Egypt's prosecutor-general to investigate allegations being made by a police officer that upwards of 900,000 votes were delivered to Ahmed Shafik by the Egyptian Ministry of the Interior. "We will present an appeal on behalf of candidate Hamdeen Sabahi … to the presidential electoral committee, citing a series of irregularities … that have affected the outcome of the first round," El-Islamboly told Reuters News Agency.
Meanwhile, there is a tendency among some to back the Brotherhood candidate Morsi against Shafik, who is seen as a throwback to the days of Mubarak. Egyptian Sheikh Yusuf al-Qaradawi has thrown his support behind Morsi, stating that the race is not between an Islamist and a non-Islamist, but between "the revolution and the enemies of the revolution."
Analysts on the ground are describing the situation as extremely "fluid", stating that things could quickly take a turn for the worse. On Twitter, there's renewed talk of mass protests, with many calling for a return to Tahrir square, the symbolic center of the 2011 Egyptian revolution that ousted longtime dictator Hosni Mubarak.